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