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 |