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