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