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