1    | /***************************************
2    |   $Revision: 1.53 $
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, Obj_parse_t *parse, int operation, long transaction_id);
69   | 
70   | static GSList *ud_split_attribute(GSList *attr_list, Attribute_t *attr);
71   | static void ud_parse_init(SQ_connection_t *sql_connection, Obj_parse_t *parse);
72   | static void ud_parse_free(Obj_parse_t *parse);
73   | static int line_continuation(char *line);
74   | static GString *escape_apostrophes(GString *text);
75   | static Object_t *UD_parse_object(Obj_parse_t *parse, char *line_buff);
76   | static int ud_replace_substring(GString *gstring, char *old, char *new);
77   | static int ud_normalize(Obj_parse_t *parse);
78   |                                                                                                 
79   | /* Delimiters that separate list members, both RPS(,) and legacy( ) */
80   | #define ATTR_DELIMITERS " ,\t"
81   | 
82   | 
83   | static void ud_parse_init(SQ_connection_t *sql_connection, Obj_parse_t *parse){
84   |      bzero(parse, sizeof(Obj_parse_t));
85   |      parse->garbage = 0;
86   |      parse->sql_connection = sql_connection;     
87   | }
88   | 
89   | static void ud_parse_free(Obj_parse_t *parse){
90   |      UT_free(parse->nic);	
91   |      UT_free(parse->object_name);
92   | }
93   | 
94   | 
95   | /******************************************************************
96   | *  int line_continuation()                                        *
97   | *                                                                 *
98   | *  Checks if the line starts with one of line continuation signs  *
99   | *  Returns 1 if this is line continuation, 0 otherwise            *
100  | *                                                                 *
101  | * ****************************************************************/
102  | static int line_continuation(char *line)
103  | {
104  |  switch(*line) {
105  | 	case ' ':
106  | 	case '\t':
107  | 	case '+':
108  | 	         return(1); /* these indicate line continuation */
109  | 	default: return(0); 
110  |  }
111  | 
112  | }
113  | 
114  | /******************************************************************
115  | *  int ud_replace_substring()                                     *
116  | *                                                                 *
117  | *  replace a subsring in a string                                 *
118  | *  Returns -1 if no match was found                               *
119  | *                                                                 *
120  | * ****************************************************************/
121  | static int ud_replace_substring(GString *gstring, char *old, char *new)
122  | {
123  |  int i, i0=-1, j=0;
124  |  
125  | 	for (i=0; ((i < gstring->len) && (old[j] != '\0')); i++){
126  | 		if(gstring->str[i]==old[j]){
127  | 			if(i0==-1)i0=i;
128  | 			j++;
129  | 		} else {
130  | 			i0=-1;
131  | 			j=0;
132  | 		}
133  | 	}
134  | 	/* if we haven't reached the end of the old substring */
135  | 	/* or no match occured return -1 */
136  | 	if((old[j] != '\0') || (i0==-1)) return (-1);
137  | 	g_string_erase(gstring, i0, j);
138  | 	g_string_insert(gstring, i0, new);
139  | 	return(0);
140  | }
141  | 
142  | /************************************************************
143  | *                                                           *
144  | * The function to splits attribute value into multiple      *
145  | * words and appends them as attr_type - attr_valu pairs     *
146  | * to the attr_list                                          *
147  | *                                                           *
148  | *                                                           *
149  | ************************************************************/
150  | static GSList *ud_split_attribute(GSList *attr_list, Attribute_t *attr)
151  | {
152  |  char *token;
153  |  char *split;
154  |  char *value, *n;
155  |  Attribute_t *attr_split;
156  |  GSList *the_list = attr_list;
157  | 
158  | 
159  |   /* check for line continuation (+) */
160  |   if (strncmp(attr->value, "+", 1) == 0) attr->value++;
161  |   /* check for end-of-line comments */
162  |   n = index(attr->value, '#');
163  |       /* if there is no comment check for trailing \n */
164  |   if(n == NULL) n = index(attr->value, '\n');
165  |   /* now copy the clean value into the attribute */
166  |   if(n == NULL) value = g_strdup(attr->value); 
167  |   else  value = g_strndup(attr->value, (n - attr->value));
168  |      
169  |   token=value;
170  |   while((split=strsep(&token, ATTR_DELIMITERS))){
171  |     if (*split != '\0'){	  
172  |       attr_split = attribute_new1(attr->type, split);
173  |       if (attr_split) the_list = g_slist_append(the_list, attr_split);
174  |     }
175  |   }
176  |   free(value);
177  |   attribute_free(attr, NULL);
178  | 
179  |  return(the_list);
180  | }
181  | 
182  | /* XXX */
183  | static void each_attribute_print(void *element_data, void *tr_ptr)
184  | {
185  | 
186  | Attribute_t *attr = (Attribute_t *)element_data;
187  | 
188  |  fprintf(stderr, "[%d|%s]\n", attr->type, attr->value);
189  | 
190  | }
191  | 
192  | /* XXX */
193  | static void print_object(Object_t *obj)
194  | {
195  |   g_slist_foreach(obj->attributes, each_attribute_print, NULL);	
196  |   fprintf(stderr, ">>>>>\n%s\n", obj->object->str);
197  | }
198  | 
199  | 
200  | /******************************************************************
201  | * GString *escape_apostrophes()                                   *
202  | * Escapes apostrophes in the text so they do not confuse printf   *
203  | * functions and don't corrupt SQL queries                         *
204  | *                                                                 *
205  | * *****************************************************************/
206  | static GString *escape_apostrophes(GString *text) {
207  |   int i;
208  |   for (i=0; i < text->len; i++) {
209  |     if ((text->str[i] == '\'') || (text->str[i] == '\\')) {
210  |       text = g_string_insert_c(text, i, '\\');
211  |       i++;
212  |     }
213  |   }
214  |  return(text); 
215  | } /* escape_apostrophes() */
216  | 
217  | 
218  | /******************************************************************
219  | * Line_Type_t line_type(e)                                        *
220  | * Determines the line type analysing the first letters            *
221  | *                                                                 *
222  | * ****************************************************************/
223  | static Line_Type_t line_type(const char *line, long *transaction_id) {
224  | 
225  |   if (strncmp(line, "# EOF", 4) == 0) return(LINE_EOF);
226  |   if (strncmp(line, "#", 1) == 0)     return(LINE_COMMENT);
227  |   if (strcmp(line, "\n") == 0)        return(LINE_EMPTY);
228  |  
229  |   if (strncmp(line, "ACK", 3) == 0) {
230  |     *transaction_id = atol(line+3);	  
231  |     return(LINE_ACK);
232  |   }
233  |   if (strncmp(line, "ADD_OVERRIDE", 12) == 0) {
234  |     *transaction_id = atol(line+12);	  
235  |     return(LINE_OVERRIDE_ADD);
236  |   }
237  |   if (strncmp(line, "UPD_OVERRIDE", 12) == 0) {
238  |     *transaction_id = atol(line+12);	  
239  |     return(LINE_OVERRIDE_UPD);
240  |   }
241  |   if (strncmp(line, "DEL_OVERRIDE", 12) == 0) {
242  |     *transaction_id = atol(line+12);	  
243  |     return(LINE_OVERRIDE_DEL);
244  |   }
245  |  
246  |   if (strncmp(line, "ADD", 3) == 0) {
247  |     *transaction_id = atol(line+3);
248  |     return(LINE_ADD);
249  |   }
250  |   if (strncmp(line, "UPD", 3) == 0) {
251  |     *transaction_id = atol(line+3);
252  |     return(LINE_UPD);
253  |   }
254  |   if (strncmp(line, "DEL", 3) == 0) {
255  |     *transaction_id = atol(line+3); 
256  |     return(LINE_DEL);
257  |   }
258  |  
259  | /* Otherwise this is an attribute */  
260  |     return(LINE_ATTRIBUTE);
261  | 
262  | } /* line_type() */
263  | 
264  | /******************************************************************
265  | * Object_t *UD_parse_object()                                     *
266  | *                                                                 *
267  | * Parses the object accepting line by line                        *
268  | * Stores the object and list of attributes in Obj_parse_t struct  *
269  | *                                                                 *
270  | * ****************************************************************/
271  | static Object_t *UD_parse_object(Obj_parse_t *parse, char *line_buff)
272  | {
273  | GString *g_line_buff;
274  | Attribute_t *attr;
275  |  
276  |   /* in case of garbage */
277  |   if(parse->garbage) return(NULL);
278  |   
279  |   if(parse->obj==NULL) {
280  |   /* which means this is the start - create a new object*/
281  | 	  parse->obj = object_new(line_buff);
282  | 	  /* if this is not a class attribute - this is a garbage till the next blank line */
283  | 	  if(parse->obj==NULL) {
284  | 		  parse->garbage = 1; 
285  | 		  return(NULL);
286  | 	  }
287  |   }
288  |   
289  |   /* allocate buffer or the string */	  
290  |   if ((g_line_buff = g_string_sized_new(STR_XXL)) == NULL){ 
291  |       ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring");
292  |       die; 
293  |   }
294  |   /* copy the input */
295  |   g_string_sprintf(g_line_buff, "%s", line_buff);
296  |   /* escape apostrophes in the input line */
297  |   g_line_buff=escape_apostrophes(g_line_buff);
298  |    
299  |   attr = attribute_new(g_line_buff->str);
300  |   if (attr){
301  |     /* save this attribute for future line continuations */
302  |     /* skip attributes that are not stored in SQL database */	  
303  |     if(DF_get_update_query_type(attr->type)==UD_NULL_){
304  |      parse->current_attr = NULL;
305  |      attribute_free(attr, NULL);
306  |     }
307  |     else {
308  |      parse->current_attr = attr;	    
309  |      if((attr->type == A_MB) || (attr->type == A_NH)) {
310  |         /* insert it at the beginning */
311  | 	/* mnt-by should go before member-of to allow correct membership autorization (still done in RIPupd) */
312  |         /* nic-hdl should go before any admin-c, tech-c to prevent errrors in self referencing role objects  */
313  | 	parse->obj->attributes = g_slist_insert(parse->obj->attributes, parse->current_attr, 1);
314  |      } else {
315  |         /* append it to the attribute list */
316  |         parse->obj->attributes = g_slist_append(parse->obj->attributes, parse->current_attr);
317  |      }
318  |     }
319  |   }
320  |   /* if this is not a valid attribute, then this may be unknown attr or line continuation */
321  |   else 
322  |    if(line_continuation(g_line_buff->str)) 
323  |    {
324  |     /* if parse->attr == NULL, then this attr is not stored in SQL database - skip it */
325  |     if(parse->current_attr){
326  |     /* this is continuation of a previous attribute */	  
327  |      char *line_value, *new_value;
328  |      char *n;  
329  |      /* normalize and append to current_attr->value */
330  |      /* check for end-of-line comments */
331  |      n = index(g_line_buff->str, '#');
332  |      /* if there is no comment check for trailing \n */
333  |      if(n == NULL) n = index(g_line_buff->str, '\n');
334  |      /* now copy the clean value into the attribute */
335  |      if(n == NULL) line_value = g_strdup(g_line_buff->str); 
336  |      else  line_value = g_strndup(g_line_buff->str, (n - g_line_buff->str));
337  |      /* replace line continuation character with ' ' */
338  |      *line_value = ' ';
339  |      new_value = g_strconcat(parse->current_attr->value, line_value, NULL);
340  |      parse->current_attr = attribute_upd(parse->current_attr, parse->current_attr->type, new_value);
341  |      UT_free(new_value);
342  |      UT_free(line_value); 
343  |     }
344  |    }
345  |   /* otherwise, this is unknown attribute, so we need to break the continuation */
346  |    else parse->current_attr=NULL;
347  |   /* copy the line into object no matter whether it is an attribute or not (continualtion, etc.) */
348  |   g_string_sprintfa(parse->obj->object, "%s", g_line_buff->str);
349  |   g_string_free(g_line_buff, TRUE);
350  |   return(parse->obj);
351  | }  
352  | 
353  | /******************************************************************
354  | * int each_attribute_2_pass()                                     *
355  | *                                                                 *
356  | * Assigns nic-handles in case of AUTO                             *
357  | * Splits attributes                                               *
358  | *                                                                 *                                                                 *
359  | ******************************************************************/
360  | void each_attribute_2_pass(void *element_data, void *ptr)
361  | {
362  | Attribute_t *attr = (Attribute_t *)element_data;
363  | Obj_parse_t *parse = (Obj_parse_t *)ptr;
364  | 
365  | 
366  |  /* Strip the white space */
367  |  g_strstrip(attr->value);
368  | 
369  |  switch(attr->type){
370  |    case A_NH: /* nic-hdl */
371  |           /*  Parse the string into nh structure */
372  |           /*  In case of an AUTO NIC handle check the ID in the database */
373  |           /* Possible errors leave to core processing */
374  |           if(NH_parse(attr->value, &parse->nh_ptr) == 0) { 
375  |            /* this is an AUTO nic handle */
376  | /*           ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s parsing nic handle: [%s]", UD_TAG, attr->value);*/
377  |            /* Check if we can allocate it */  
378  |             if(NH_check(parse->nh_ptr, parse->sql_connection)>0){
379  |             /* Convert nh to the database format */  
380  |               parse->nic = NH_convert(parse->nh_ptr);
381  | /*           ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s parsed and converted nic handle: [%s]", UD_TAG, nic); */
382  |               /* replace the auto nic with a real one */
383  | 	      if(ud_replace_substring(parse->obj->object, attr->value, parse->nic)==-1) {
384  | 		     ER_perror(FAC_UD, UD_BUG, "cannot replace auto nic handle"); 
385  | 		     die;
386  | 	      }
387  | 	      /* Update the attribute */
388  |               attr = attribute_upd(attr, attr->type, parse->nic);
389  | 	      
390  |             }
391  |           }
392  |             parse->new_attr_list = g_slist_append(parse->new_attr_list, attr);
393  |             break;
394  |    case A_PN: /* person */
395  |    case A_RO: /* role */
396  |    case A_MR: /* mbrs-by-ref */
397  |    case A_MB: /* mnt-by */
398  |    case A_ML: /* mnt-lower */
399  |    case A_CT: /* cross-mnt */
400  |    case A_MO: /* member-of */
401  |    case A_SD: /* sub-dom */
402  |    case A_RZ: /* rev-srv */
403  |    case A_NS: /* nserver */
404  |             parse->new_attr_list = ud_split_attribute(parse->new_attr_list, attr);
405  | 	    break;
406  |    default:
407  |             parse->new_attr_list = g_slist_append(parse->new_attr_list, attr);
408  | 	    break;
409  |  }
410  | 
411  | }
412  | 
413  | 		  
414  | /******************************************************************
415  | * int ud_normalize()                                              *
416  | *                                                                 *
417  | * function accepts Obj_parse_t structure with attribute list      *
418  | *                                                                 *
419  | * It makes another pass to normalize object                       *
420  | * Normalization includes:                                         *
421  | *                                                                 *
422  | * 1) reorder attributes                                           *
423  | *  . mnt-by should go before member-of to allow correct           * 
424  | *      membership autorization (still done in RIPupd)             *
425  | *  . nic-hdl should go before any admin-c, tech-c to prevent      * 
426  | *       errrors in self referencing role objects                  *
427  | * 2) in case of pn/ro check nic handle and replace it in          *
428  | *     the obj->object if needed (AUTO)                            * 
429  | * 3) split some attributes that allow lists                       *
430  | ******************************************************************/
431  | static int ud_normalize(Obj_parse_t *parse)
432  | {
433  | GSList *old_attr_list;
434  | GSList *first_element;
435  | Attribute_t *class_attr;
436  | 
437  | 	old_attr_list = parse->obj->attributes;
438  | 	/* get class attribute - the first one */
439  | 	first_element = g_slist_nth(old_attr_list, 0);
440  | 	class_attr = (Attribute_t *)first_element->data;
441  | 	/* save object name for reporting and checking ref integrity for names (legacy) */ 	
442  | 	parse->object_name = g_strdup(class_attr->value);
443  | 	/* reorder the attributes mnt-by and nic-hdl come first */
444  | 	/*g_slist_foreach(old_attr_list, each_attribute_1_pass, parse); 
445  | 	g_slist_free(old_attr_list);
446  | 	old_attr_list = parse->new_attr_list; */
447  | 	parse->new_attr_list = NULL;
448  | 	/* assign auto nic-hdl and slit attributes */
449  | 	g_slist_foreach(old_attr_list, each_attribute_2_pass, parse);
450  | 	g_slist_free(old_attr_list);
451  | 	parse->obj->attributes = parse->new_attr_list;
452  | 
453  | 	return(1);
454  | }
455  | 
456  | 
457  | /******************************************************************
458  | * report_transaction()                                            *
459  | *                                                                 * 
460  | * Prints error report to the log                                  *
461  | *                                                                 *
462  | * reason - additional message that will be included               *
463  | *                                                                 *
464  | * *****************************************************************/
465  | static int report_transaction(Transaction_t *tr, long transaction_id,  Log_t *log, ut_timer_t *psotime, char *reason)
466  | {
467  | int result=0;
468  | ut_timer_t fotime;
469  | float timediff;
470  | const char *class_name = DF_class_type2name(tr->class_type);
471  | char *primary_key = tr->K->str;
472  | 
473  | 
474  |  /* calculate statistics */
475  |   UT_timeget(&fotime);
476  |   timediff = UT_timediff(psotime, &fotime);
477  |  
478  |  if(tr->succeeded==0) {
479  |   result=tr->error;
480  |   log->num_failed++;
481  |   ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] %.2fs FAILED [%s:%s][%s]", transaction_id, timediff, class_name, primary_key, reason);
482  | /*  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)); */
483  |   if(result & ERROR_U_OBJ) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: referential integrity error", transaction_id);
484  |   if(result & ERROR_U_AUT) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: authentication error", transaction_id);
485  |   if(result & ERROR_U_BADOP) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: unsupported operation", transaction_id);
486  |   if(result & ERROR_U_COP) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: conflicting operation", transaction_id);
487  |   if(result & ERROR_U_NSUP) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: this type is not supported", transaction_id);
488  |   ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] object: details [%s]", transaction_id, (tr->error_script)->str);
489  |   result=1; /* # of failures */
490  |  }
491  |  else {
492  |   result=0;
493  |   log->num_ok++;
494  | /*  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)); */
495  |   ER_inf_va(FAC_UD, ASP_UD_OBJ, "[%ld] %.2fs OK     [%s:%s][%s]", transaction_id, timediff, class_name, primary_key, reason);
496  |   ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] object: details [%s]", transaction_id, (tr->error_script)->str);
497  |  }
498  |                                                                                                                                                
499  |  return(result);
500  | }/* report_transaction() */
501  | 
502  | 
503  | 
504  | /************************************************************
505  | * process_nrtm()                                            *
506  | *                                                           *
507  | * Process object in NRTM client mode                        *
508  | *                                                           *
509  | * nrtm - pointer to _nrtm structure                         *
510  | * log - pointer to Log_t structure                          *
511  | * object_name - name of the object                          * 
512  | * operation - operation code (OP_ADD/OP_DEL)                *
513  | *                                                           *
514  | * Returns:                                                  *
515  | * 1  - okay                                                 *
516  | * <0 - error                                                *
517  | *                                                           *
518  | ************************************************************/
519  | 
520  | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, int operation)
521  | {
522  | int result=0;
523  | int dummy=0;
524  | struct _nrtm *nrtm = ud_stream->nrtm;
525  | long serial_id;
526  | Log_t *log_ptr= &(ud_stream->log);
527  | ut_timer_t sotime;
528  | int ta_upd_nhr;
529  | 
530  |     /* Start timer for statistics */
531  |     UT_timeget(&sotime);
532  | 
533  |   /* We allow NRTM updates for some inconsistent objects                  */
534  |   /* One of the examples is reference by name which looks like nic-handle */
535  |   /* For this purpose we allow dummy creation when updating an object     */
536  |   /* We also check for dummy allowance when deleting an object            */
537  |   /* this is done to allow deletion of person objects referenced by name  */
538  | 
539  |   tr->mode|=B_DUMMY;
540  |   if(IS_NO_NHR(tr->mode))ta_upd_nhr=0; else ta_upd_nhr = TA_UPD_NHR;
541  |   
542  |   switch (operation) {
543  |   
544  |   case OP_ADD:
545  |     if(nrtm->tr){ /* DEL ADD => saved*/
546  |       if(tr->object_id==0) { 
547  | 	/* object does not exist in the DB */      
548  | 	/* delete the previous(saved) object*/
549  |         object_process(nrtm->tr); 
550  |         /* create DEL serial */
551  | 	UD_lock_serial(nrtm->tr);
552  | 	serial_id = UD_create_serial(nrtm->tr);
553  | 	CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr); 
554  | 	UD_commit_serial(nrtm->tr);
555  | 	UD_unlock_serial(nrtm->tr);
556  |         /* Mark TR as clean */
557  | 	TR_mark_clean(nrtm->tr);
558  |         /* log the transaction */
559  | 	result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
560  |         
561  |         object_free(nrtm->tr->object);
562  |         transaction_free(nrtm->tr); nrtm->tr=NULL;
563  |         
564  | 	/* Create an object and update NHR */
565  |         tr->action=(TA_CREATE | ta_upd_nhr);
566  | 	/* restart the timer for statistics */
567  | 	UT_timeget(&sotime);
568  |         object_process(tr); /* create a new one*/
569  | 	/* create ADD serial */
570  |         UD_lock_serial(tr);
571  | 	serial_id = UD_create_serial(tr); 
572  | 	CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
573  | 	UD_commit_serial(tr);
574  | 	UD_unlock_serial(tr);
575  | 	/* Mark TR as clean */
576  | 	TR_mark_clean(tr);
577  |         /* log the transaction */
578  | 	result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
579  |       }
580  |       else { 
581  |       /* object already exists in the DB - update or dummy replacement*/
582  |         /*compare the two, may be we may collapse operations*/
583  |         if(tr->object_id==nrtm->tr->object_id) {
584  |           /* DEL-ADD ->> UPDATE */ 
585  | 	  object_free(nrtm->tr->object);
586  |           transaction_free(nrtm->tr); nrtm->tr=NULL;
587  |           tr->action=TA_UPD_CLLPS;
588  |           object_process(tr);
589  | 	  /* create DEL+ADD serial records */
590  | 	  UD_lock_serial(tr);
591  | 	  tr->action=TA_DELETE; serial_id = UD_create_serial(tr);
592  | 	  result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:DEL(UPD)");
593  | 
594  | 	  /* restart the timer for statistics */
595  |           UT_timeget(&sotime);
596  | 	  tr->sequence_id++;
597  |           tr->action=TA_CREATE; serial_id = UD_create_serial(tr);
598  | 	  CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
599  | 	  UD_commit_serial(tr);
600  | 	  UD_unlock_serial(tr);
601  | 	  /* Mark TR as clean */
602  | 	  TR_mark_clean(tr);
603  | 	  result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD(UPD)");
604  |         }
605  |         else { /* this should be a dummy object in the database(that we are going to replace with the real one */
606  |         /* or an interleaved operation*/
607  |           object_process(nrtm->tr); /* delete the previous(saved) object*/
608  |           /* create a DEL serial record */
609  | 	  UD_lock_serial(nrtm->tr);
610  | 	  serial_id = UD_create_serial(nrtm->tr); 
611  | 	  CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
612  | 	  UD_commit_serial(nrtm->tr);
613  | 	  UD_unlock_serial(nrtm->tr);
614  | 	  /* Mark TR as clean */
615  | 	  TR_mark_clean(nrtm->tr);
616  | /*	  result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved)");*/
617  |           /* log the transaction */
618  | 	  result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
619  | 
620  | 
621  |           object_free(nrtm->tr->object);
622  |           transaction_free(nrtm->tr); nrtm->tr=NULL;
623  | 
624  | 	  /* restart the timer for statistics */
625  |           UT_timeget(&sotime);
626  | 	  
627  |           tr->action=TA_UPDATE;
628  |           /* check if we are replacing a dummy object */
629  | 	  dummy=isdummy(tr);
630  |           /* If we are replacing dummy with a real object update NHR */
631  |           if(dummy==1) tr->action = (ta_upd_nhr | TA_UPD_DUMMY);
632  | /*        fprintf(stderr,"UPDATE next(dummy)\n"); */
633  |           object_process(tr); /* create a new one*/
634  | /*          result=report_transaction(tr, log_ptr, object_name, "NRTM:ADD:While creating new"); */
635  | 	  /* For serials this is CREATE operation. Increase sequence to have it correct in serals */
636  |           if(dummy==1) { tr->action=TA_CREATE; tr->sequence_id++; } 
637  | 	  /* create ADD serial record */
638  |           UD_lock_serial(tr);
639  | 	  serial_id = UD_create_serial(tr); 
640  | 	  CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
641  |           UD_commit_serial(tr);
642  | 	  UD_unlock_serial(tr);
643  | 	  /* Mark TR as clean */
644  | 	  TR_mark_clean(tr);
645  |           /* log the transaction */
646  |           result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
647  | 
648  |         }
649  |       }
650  |     }
651  |     else { /* ADD ADD =>brand new object*/
652  |       if(tr->object_id==0) {
653  | /*      fprintf(stderr,"CREATE new\n");*/
654  |         /* Create an object and update NHR */
655  |         tr->action=(TA_CREATE | ta_upd_nhr);
656  |         object_process(tr);
657  |         /* create ADD serial */
658  | 	UD_lock_serial(tr);
659  | 	serial_id = UD_create_serial(tr); 
660  | 	CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
661  |         UD_commit_serial(tr);
662  | 	UD_unlock_serial(tr);
663  | 
664  | 	/* Mark TR as clean */
665  | 	TR_mark_clean(tr);
666  |         /* log the transaction */
667  |         result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
668  |       }
669  |       else { /* object already exists in the database */
670  | 	/* this may happen because of dummies*/
671  | 	/* or with some implementations of mirroring protocol that have atomic update */
672  | 	/* instead of add + del */
673  |         tr->action=TA_UPDATE;
674  |         dummy=isdummy(tr);
675  |         /* If we are replacing dummy with a real object update NHR */
676  |         if(dummy==1) tr->action = ( ta_upd_nhr| TA_UPD_DUMMY);
677  |         object_process(tr);
678  |         /* For serials this is CREATE operation. Increase sequence to have it correct in serals */
679  |         if(dummy==1) { tr->action=TA_CREATE; tr->sequence_id++; }
680  | 	/* create ADD serial record */
681  | 	UD_lock_serial(tr);
682  | 	serial_id = UD_create_serial(tr); 
683  | 	CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
684  |         UD_commit_serial(tr);
685  | 	UD_unlock_serial(tr);
686  | 	/* Mark TR as clean */
687  | 	TR_mark_clean(tr);
688  |         /* log the transaction */
689  |         result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
690  | 	
691  |       } 
692  |     }
693  |     break;
694  |     
695  |   case OP_DEL:
696  |     if(nrtm->tr){ /*DEL DEL =>saved */
697  |       /* check this is not a deletion of the same object twise */
698  |       /* this should not happen but we cannot trust foreign sources */
699  |       /* in such case process saved transaction but fail the current one */
700  |       if(nrtm->tr->object_id == tr->object_id) tr->object_id=0;
701  | 	    
702  |       object_process(nrtm->tr); /* delete the previous(saved) object*/
703  |       /* create DEL serial record */
704  |       UD_lock_serial(nrtm->tr);
705  |       serial_id = UD_create_serial(nrtm->tr);  
706  |       CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
707  |       UD_commit_serial(nrtm->tr);
708  |       UD_unlock_serial(nrtm->tr);
709  |       /* Mark TR as clean */
710  |       TR_mark_clean(nrtm->tr);
711  |       /* log the transaction */
712  |       result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
713  |       
714  |       object_free(nrtm->tr->object);
715  |       transaction_free(nrtm->tr); nrtm->tr=NULL;
716  |     }
717  |     /* save the real object (not a dummy one ) */
718  |     if(tr->object_id>0 && !isdummy(tr)){ 
719  |       /* save the object*/
720  |       tr->action=(TA_DELETE | ta_upd_nhr);
721  |       nrtm->tr=tr;
722  |       return(0);
723  |     }
724  |     else { /* this is an error - Trying to DEL non-existing object*/
725  |       tr->succeeded=0; tr->error|=ERROR_U_COP;
726  |       tr->action=(TA_DELETE | ta_upd_nhr);
727  |       /* create and initialize TR record for crash recovery */
728  |       TR_create_record(tr);
729  |       /* create DEL serial record anyway */
730  |       UD_lock_serial(tr);
731  |       serial_id = UD_create_serial(tr); 
732  |       CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
733  |       UD_commit_serial(tr);
734  |       UD_unlock_serial(tr);
735  |       /* Mark TR as clean */
736  |       TR_mark_clean(tr);
737  |       /* log the transaction */
738  |       result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:DEL: non-existing object");
739  |       
740  |     }
741  |     break;
742  |   
743  |   default:
744  |     tr->succeeded=0; tr->error |=ERROR_U_BADOP;
745  |     break;  
746  |   }
747  | 
748  |  /* Free resources */  
749  |   object_free(tr->object);
750  |   transaction_free(tr);
751  |   
752  |   return(result);
753  | } /* process_nrtm() */
754  | 
755  | 
756  | 
757  | /************************************************************
758  | * process_updates()                                         *
759  | *                                                           *
760  | * Process object in update mode                             *
761  | *                                                           *
762  | * ud_stream - pointer to UD_stream structure                *
763  | * object_name - name of the object                          *
764  | * operation - operation code (OP_ADD/OP_DEL)                *
765  | *                                                           *
766  | * Note:                                                     *
767  | * Frees tr and tr->obj on exit                              *
768  | *                                                           *
769  | * Returns:                                                  *
770  | * 0  - okay                                                 *
771  | * <0- number of failed objects                              *
772  | *                                                           * 
773  | ************************************************************/
774  | 
775  | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, int operation)
776  | {
777  | int result=0;
778  | Log_t *log_ptr= &(ud_stream->log);
779  | int dummy=0;
780  | ut_timer_t sotime;
781  | int ta_upd_nhr;
782  | char *reason;
783  | 
784  |     /* Start timer for statistics */
785  |     UT_timeget(&sotime);
786  |     if(IS_NO_NHR(tr->mode))ta_upd_nhr=0; else ta_upd_nhr = TA_UPD_NHR;
787  | 
788  |     switch(operation) {
789  |     /* Compare operations and report an error if they do not match */    
790  |     case OP_ADD:
791  |       if(tr->object_id!=0) { /* trying to create, but object exists */
792  |         tr->succeeded=0; tr->error|=ERROR_U_COP;
793  | 	reason="U:ADD:object already exists";
794  |         UD_ack(tr); /* Send a NACK */
795  |       } else {
796  |        /* Action: create the object and update NHR */
797  |         tr->action=(TA_CREATE | ta_upd_nhr);
798  | 	reason="U:ADD";
799  |         object_process(tr);
800  |       }
801  |       break;
802  |     case OP_UPD:
803  |       if(tr->object_id==0) { /* trying to update non-existing object*/
804  |         tr->succeeded=0; tr->error|=ERROR_U_COP;
805  | 	reason="U:UPD:non-existing object";
806  |         UD_ack(tr); /* Send a NACK */
807  |       } else {
808  |         tr->action=TA_UPDATE;
809  | 	reason="U:UPD";
810  |         dummy=isdummy(tr);
811  |         /* If we are replacing dummy with a real object update NHR */
812  |         if(dummy==1) tr->action = (ta_upd_nhr | TA_UPD_DUMMY);
813  |         object_process(tr);
814  |       }
815  |       break;
816  | 
817  |     case OP_DEL:        
818  |       if(tr->object_id==0) { /* trying t delete non-existing object*/
819  |         tr->succeeded=0; tr->error|=ERROR_U_COP;
820  | 	reason="U:DEL:non-existing object";
821  | 	UD_ack(tr);
822  |       } else {
823  |         tr->action=(TA_DELETE | ta_upd_nhr);
824  | 	reason="U:DEL";
825  |         object_process(tr);
826  |       }
827  |       break;
828  |                 
829  |     default:                
830  |       /* bad operation for this mode if not standalone */
831  |       if(IS_STANDALONE(tr->mode)) {
832  |         if(tr->object_id==0){
833  | 	  tr->action=(TA_CREATE | ta_upd_nhr); 
834  | 	  reason="U:ADD";
835  | 	}
836  | 	else {
837  | 	  tr->action=TA_UPDATE;
838  | 	  reason="U:UPD";
839  | 	}
840  |         object_process(tr);
841  |       }
842  |       else {
843  |         tr->succeeded=0; 
844  |         tr->error|=ERROR_U_BADOP;
845  | 	reason="U:bad operation";
846  |         UD_ack(tr); /* Send a NACK */ 
847  |       }
848  |       break;
849  |     }
850  |    /* If not in standalone mode create serial and copy error transcript */ 
851  |     if(!IS_STANDALONE(tr->mode)) {
852  |       if(tr->succeeded){
853  | 	      /* we don't want to generate DEL serial for dummy replacement*/
854  | 	      if(dummy==1) { tr->action=TA_CREATE; tr->sequence_id++; }
855  |               UD_lock_serial(tr);
856  | 	      UD_create_serial(tr); 
857  | 	      CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
858  | 	      UD_commit_serial(tr);
859  | 	      UD_unlock_serial(tr);
860  | 	      /* Mark the TR as clean */
861  |               TR_mark_clean(tr);
862  |       }
863  |     }  
864  |    
865  |    /* Make a report. U stands for update stream. No reason */
866  |     result=report_transaction(tr, tr->transaction_id, log_ptr, &sotime, reason);
867  | 
868  |    /* Free resources */   
869  |     object_free(tr->object);
870  |     transaction_free(tr);
871  |     
872  |     return(result);
873  |         
874  | } /* process_updates() */
875  | 
876  | 
877  | /************************************************************
878  | *                                                           *
879  | * int process_transaction()                                 *
880  | *                                                           *
881  | * Processes the transaction                                 *
882  | *                                                           *
883  | * ud_stream - pointer to UD_stream_t structure              *
884  | *                                                           *
885  | * Returns:                                                  *
886  | * 0 - no error                                              *
887  | * <0- number of failed objects                              *
888  | *                                                           *
889  | ************************************************************/
890  | 
891  | /* It frees the obj */
892  | 
893  | static int process_transaction(UD_stream_t *ud_stream, 
894  |                          Obj_parse_t *parse,
895  | 			 int operation,
896  | 			long transaction_id)
897  | {
898  | Transaction_t *tr = NULL;
899  | Object_t *obj = parse->obj;
900  | int result;
901  | 
902  | /* check if the requested transaction has already been processed */
903  | /* this may happen in case of crash. If so, just send an ack and return */
904  |  if(TR_check(ud_stream->db_connection, transaction_id, (ud_stream->condat).sock))return(1);
905  | 
906  | /* start new transaction now */ 
907  |  tr = transaction_new(ud_stream->db_connection, obj->type);
908  | 
909  | /* Return with error if transaction cannot be created */ 
910  |  if (tr == NULL) die;
911  |  
912  |  tr->mode=ud_stream->ud_mode;
913  |  tr->load_pass=ud_stream->load_pass;
914  |  tr->object=obj;
915  |  tr->nh=parse->nh_ptr;
916  |  tr->source_hdl=ud_stream->source_hdl;
917  |  tr->socket=(ud_stream->condat).sock;
918  |  tr->transaction_id=transaction_id;
919  |  
920  | /* We perform no commit/rollback in the loader mode, so thread_id should be set to 0 */
921  |  if(ud_stream->load_pass!=0) { tr->thread_ins=0; tr->thread_upd=0; }
922  |     
923  | /* For the first load pass we only create objects */ 
924  |  if(ud_stream->load_pass==1) {
925  | 	 Object_t *obj=tr->object;
926  | 	 /* still we need to fill tr->K (last.pkey) field */ 
927  | 	 g_slist_foreach(obj->attributes, ud_each_primary_key_select, tr);
928  | 	 tr->object_id=0;
929  |  }
930  |  else tr->object_id=get_object_id(tr);
931  |  
932  | /* Object cannot be retrieved */
933  |  if(tr->object_id==-1) { /* DB error*/
934  |     tr->succeeded=0;
935  |     tr->error |= ERROR_U_DBS;
936  |     ER_perror(FAC_UD, UD_SQL, "%s: Object cannot be retrieved", parse->object_name);
937  |     die;
938  |     transaction_free(tr);
939  |     object_free(obj);
940  |  }
941  | /* save the name of person/role as we need it for referential */
942  | /* integrity check when deleting the object against names. */
943  | /* This is needed to support legacy references by name rather */
944  | /* then by nic_hdl */
945  |   if((tr->class_type==C_PN) || (tr->class_type==C_RO)){
946  |     /* Save the value */
947  |     tr->save=g_strdup(parse->object_name);
948  | /*    attribute_free(attr, NULL); */
949  |   }
950  |                                                
951  | /* Process transaction. tr and obj are freed inside the process_* functions */
952  | 
953  |  if(IS_UPDATE(ud_stream->ud_mode))
954  |  /* We are in update mode */
955  |     result=process_updates(ud_stream, tr, operation);
956  |  else
957  |  /* We are in NRTM mode */   
958  |     result=process_nrtm(ud_stream, tr, operation);
959  | 
960  |  return(result);
961  | 
962  | }          
963  |           
964  | 
965  | /************************************************************
966  | *                                                           *
967  | * int UD_process_stream(UD_stream_t *ud_stream)             *
968  | *                                                           *
969  | * Processes the stream                                      *
970  | *                                                           *
971  | * ud_stream - pointer to UD_stream_t structure              *
972  | *                                                           *
973  | * Returns:                                                  *
974  | * in update mode (!standalone)(1 object processed):         *
975  | * 1 - no error                                              *
976  | * <0- errors                                                *
977  | *                                                           *
978  | * in NRTM & standalone modes                                *
979  | * total number of object processed                          *
980  | *                                                           *
981  | ************************************************************/
982  | 
983  | int UD_process_stream(UD_stream_t *ud_stream)
984  | {
985  |   char line_buff[STR_XXL];
986  |   Object_t *obj = NULL;
987  |   SQ_connection_t *sql_connection;
988  |   int start_object;
989  |   int a_type;
990  |   struct _nrtm *nrtm;
991  |   Log_t *log_ptr= &(ud_stream->log);
992  |   ut_timer_t stime, ftime, sotime;
993  |   float obj_second1, obj_second10, timediff;
994  |   int result;
995  |   int operation=0;
996  |   int interrupt=0;
997  |   int do_update;
998  |   int default_ud_mode = ud_stream->ud_mode;
999  |   Line_Type_t linetype;
1000 |   Transaction_t *tr;
1001 |   long transaction_id=0; /* transaction_id (to be supplied by DBupdate and stored in Database) */
1002 |   long serial_id;
1003 |   Obj_parse_t obj_parse; /* the structure used to parse a text object */
1004 |   
1005 |   
1006 |   
1007 |   
1008 |   nrtm=ud_stream->nrtm;
1009 |   start_object = 1;
1010 |   a_type=-1; 
1011 | 
1012 | 
1013 |   /* Check connection to the database */
1014 |   if(SQ_ping(ud_stream->db_connection)) {
1015 |    ER_perror(FAC_UD, UD_SQL, "%s", SQ_error(ud_stream->db_connection));
1016 |    die;
1017 |   }
1018 |    
1019 |   sql_connection=ud_stream->db_connection;
1020 |   
1021 |   ud_parse_init(sql_connection, &obj_parse);
1022 |   /* Start timer for statistics */
1023 |   UT_timeget(&stime);
1024 | 
1025 |  /* Main loop. Reading input stream line by line */
1026 |  /* Empty line signals to start processing an object, if we have it */ 
1027 |   while (SK_cd_gets(&ud_stream->condat, line_buff, sizeof(line_buff))>0) {
1028 | 
1029 | 
1030 |     switch (linetype=line_type(line_buff, &transaction_id)) {
1031 |       case LINE_ATTRIBUTE:
1032 |        /* parse the object line by line */
1033 |        obj = UD_parse_object(&obj_parse, line_buff);
1034 | 
1035 |       break;
1036 | 
1037 |       case LINE_COMMENT:
1038 |       break;
1039 | 
1040 |       case LINE_EOF:
1041 |       break;
1042 | 
1043 |       case LINE_ACK:
1044 |        tr = transaction_new(ud_stream->db_connection, 0);
1045 |        tr->transaction_id=transaction_id;
1046 |        TR_delete_record(tr);
1047 |        transaction_free(tr);
1048 |       break;
1049 |       
1050 |       
1051 |       case LINE_ADD:
1052 |       /* restore the default operation mode */
1053 |        operation=OP_ADD;
1054 |        ud_stream->ud_mode=default_ud_mode;
1055 |       break;
1056 |       
1057 |       case LINE_OVERRIDE_ADD:
1058 |       /* for override - switch the dummy bit on */
1059 |        operation=OP_ADD;
1060 |        ud_stream->ud_mode=default_ud_mode|B_DUMMY;
1061 |       break;
1062 |       
1063 |       case LINE_UPD:
1064 |       /* restore the default operation mode */
1065 |        operation=OP_UPD;
1066 |        ud_stream->ud_mode=default_ud_mode;
1067 |       break;  
1068 | 
1069 |       case LINE_OVERRIDE_UPD:
1070 |       /* for override - switch the dummy bit on */
1071 |        operation=OP_UPD;
1072 |        ud_stream->ud_mode=default_ud_mode|B_DUMMY;
1073 |       break;
1074 |       
1075 |       case LINE_DEL:
1076 |       /* restore the default operation mode */
1077 |        operation=OP_DEL;
1078 |        ud_stream->ud_mode=default_ud_mode;
1079 |       break; 
1080 | 
1081 |       case LINE_OVERRIDE_DEL:
1082 |       /* for override - switch the dummy bit on */
1083 |        operation=OP_DEL;
1084 |        ud_stream->ud_mode=default_ud_mode|B_DUMMY;
1085 |       break;
1086 |  
1087 |       case LINE_EMPTY:
1088 |        /* start processing the object */
1089 |         if ((obj=obj_parse.obj)) { /* if not just garbage*/
1090 | 	 /* normalize the object (reorder and split attributes */
1091 | 	 ud_normalize(&obj_parse);
1092 |  	 /* XXX */
1093 | /*	 print_object(obj); */
1094 | 
1095 |          /* start new transaction now */
1096 | 	 ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] object: [%s] ", transaction_id, obj_parse.object_name); 
1097 | 
1098 | /*	 fprintf(stderr, "transction # %ld\n", transaction_id); */
1099 |          result=process_transaction(ud_stream, &obj_parse, operation, transaction_id);
1100 |          /* process_transaction() frees tr and obj structures, */
1101 |          /* so make sure we'll not reference these objects in the future */
1102 |          operation=OP_NOOP;
1103 |          transaction_id=0;
1104 |          ud_stream->ud_mode=default_ud_mode;
1105 | 	 ud_parse_free(&obj_parse);
1106 |           
1107 |          /* this is a good place for quick interrupt */
1108 |          do_update=CO_get_do_update();
1109 |          if (do_update) interrupt=0; else interrupt=1;
1110 | 	} /* if this is a real object */
1111 | 	/* initialize the parsing structure */
1112 | 	ud_parse_init(sql_connection, &obj_parse);
1113 | 
1114 |       break;
1115 | 
1116 |       default:
1117 | 	die;
1118 |     } /* switch */
1119 |     
1120 |     /* Finish processing if interrupt has been set */
1121 |     if (interrupt) break;
1122 |   } /* Main loop of data stream processing : while */
1123 |  
1124 |  /* Some postprocessing */
1125 |   if(IS_NRTM_CLNT(ud_stream->ud_mode)){
1126 |   /* We are in NRTM mode */
1127 |   /* Clean up */
1128 | /*   fclose(ud_stream->stream); */
1129 |   /* In NRTM mode there may be a saved object that is unprocessed */   
1130 |    if(nrtm->tr){ /*saved backlog?*/
1131 |     /* restart the timer for statistics */
1132 |     UT_timeget(&sotime);
1133 |     object_process(nrtm->tr); /* delete the previous(saved) object*/
1134 | /*    result=report_transaction(nrtm->tr, &(ud_stream->log), nrtm->object_name, 
1135 |                               "NRTM:DEL:While deleting previous(saved) object"); */
1136 |     /* create DEL serial record no matter what the result is */
1137 |     UD_lock_serial(nrtm->tr);
1138 |     serial_id = UD_create_serial(nrtm->tr); 
1139 |     CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
1140 |     UD_commit_serial(nrtm->tr);
1141 |     UD_unlock_serial(nrtm->tr);
1142 |     /* Mark TR as clean */
1143 |     TR_mark_clean(nrtm->tr);
1144 |     /* log the transaction */
1145 |     result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
1146 | 
1147 |     object_free(nrtm->tr->object);
1148 |     transaction_free(nrtm->tr); nrtm->tr=NULL;
1149 |    } 
1150 |   }
1151 | 
1152 |  /* That's all. Free GString */
1153 | /*  g_string_free(g_line_buff, TRUE);*/
1154 | 
1155 |                                                                                                        
1156 |  /* Calculate some statistics */
1157 | /*  ftime=time(NULL); */
1158 |   UT_timeget(&ftime);
1159 | /*  obj_second1 = (float)(log_ptr->num_ok)/(ftime-stime);
1160 |   obj_second10 = (float)(log_ptr->num_ok+log_ptr->num_failed)/(ftime-stime); */
1161 |   timediff = UT_timediff(&stime, &ftime);
1162 |   obj_second1 = (float)(log_ptr->num_ok)/timediff;
1163 |   obj_second10 = (float)(log_ptr->num_ok+log_ptr->num_failed)/timediff;
1164 |   
1165 |   /* Print the report */
1166 |   if(IS_STANDALONE(ud_stream->ud_mode) || (!IS_UPDATE(ud_stream->ud_mode))) {
1167 | 
1168 |    ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s ******** report **********", UD_TAG);
1169 |    ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s %d objects OK (%7.4f obj/s)", UD_TAG, log_ptr->num_ok, obj_second1);
1170 |    ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s %d objects failed", UD_TAG, log_ptr->num_failed);
1171 |    ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s average processing time %7.4f obj/s (%6.2f obj/min)", UD_TAG, 
1172 |                           obj_second10, obj_second10*60);
1173 |    result=log_ptr->num_ok+log_ptr->num_failed;
1174 |   }
1175 |   return(result);
1176 | 
1177 | } /* UD_process_stream */
1178 |