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