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