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