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