modules/ud/ud_core.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- convert_if
- convert_rf
- convert_as
- convert_as_range
- convert_time
- get_set_name
- get_object_id
- get_minmax_id
- get_qresult_str
- get_field_str
- get_sequence_id
- get_ref_id
- isdummy
- isnichandle
- process_reverse_domain
- insert_reverse_domain
- update_reverse_domain
- auth_member_of
- create_dummy
- update_attr
- each_attribute_process
- each_primary_key_select
- perform_create
- perform_update
- object_process
1 /***************************************
2
3 $Revision: 1.35 $
4
5 Core functions for update lower layer
6
7 Status: NOT REVUED, NOT TESTED
8
9 Author(s): Chris Ottrey, Andrei Robachevsky
10
11 ******************/ /******************
12 Modification History:
13 andrei (17/01/2000) Created.
14 ******************/ /******************
15 Copyright (c) 2000 RIPE NCC
16
17 All Rights Reserved
18
19 Permission to use, copy, modify, and distribute this software and its
20 documentation for any purpose and without fee is hereby granted,
21 provided that the above copyright notice appear in all copies and that
22 both that copyright notice and this permission notice appear in
23 supporting documentation, and that the name of the author not be
24 used in advertising or publicity pertaining to distribution of the
25 software without specific, written prior permission.
26
27 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 ***************************************/
34 #include "ud.h"
35 #include "ud_int.h"
36 #include "ud_tr.h"
37
38 #include <sys/types.h>
39 #include <signal.h>
40 #include <time.h>
41
42 static int perform_update(Transaction_t *tr);
43
44 static int perform_create(Transaction_t *tr);
45
46 static void each_primary_key_select(void *element_data, void *result_ptr);
47
48 static void each_attribute_process(void *element_data, void *tr_ptr);
49
50 static void update_attr(Attribute_t *attr, Transaction_t *tr);
51
52 static int create_dummy(Attribute_t *attr, Transaction_t *tr);
53
54 static int auth_member_of(Attribute_t *attr, Transaction_t *tr);
55
56 /***************************************************
57 * char *s_split(char *line) *
58 * *
59 * Consequently returns words of the 'line' *
60 * When there are no words it returns NULL *
61 * You need to retreive all words ! *
62 * *
63 * NB This function damages 'line' replacing *
64 * whitespace with '\0' *
65 * *************************************************/
66 #define ATTR_DELIMITERS " ,"
67
68
69 /**********************************************************
70 * Attribute expansion/conversion functions *
71 ***********************************************************/
72 /* Convert ifaddr attribute into numbers */
73 er_ret_t convert_if(char *avalue, unsigned int *pif_address)
/* [<][>][^][v][top][bottom][index][help] */
74 {
75 char *delim;
76 ip_addr_t ip_addr;
77 er_ret_t ret;
78
79 if ((delim=index(avalue, ' '))!=NULL) *delim='\0';
80 ret=IP_addr_a2v4(avalue, &ip_addr, pif_address );
81 return(ret);
82 }
83
84
85 /* Convert refer attribute. Free host after use ! */
86 char *convert_rf(char *avalue, int *type, int *port)
/* [<][>][^][v][top][bottom][index][help] */
87 {
88 char *delim, *token;
89 char buff[STR_M];
90 char *host;
91
92 host=NULL;
93 strcpy(buff, avalue);
94 g_strchug(buff);
95 delim=index(buff, ' ');
96 *delim='\0';
97 delim++;
98
99 /* convert the type */
100 if(strcmp(buff, S_RIPE)==0)*type=RF_RIPE;
101 else if(strcmp(buff, S_INTERNIC)==0)*type=RF_INTERNIC;
102 else if(strcmp(buff, S_SIMPLE)==0)*type=RF_SIMPLE;
103 else if(strcmp(buff, S_CLIENTADDERSS)==0)*type=RF_CLIENTADDRESS;
104
105 token=delim;
106 g_strchug(token);
107 delim=index(token, ' ');
108 if(delim){
109 *delim='\0';
110 delim++;
111 }
112 /* convert the hostname */
113 host = g_strdup(token);
114
115 /* convert port number */
116 if(delim){
117 token=delim;
118 *port = atoi(token);
119 if (*port==0) *port=RF_DEF_PORT; /* default port number*/
120 } else *port=RF_DEF_PORT;
121 return(host);
122 }
123
124
125 /* Convert AS# into integer */
126 static int convert_as(char *as)
/* [<][>][^][v][top][bottom][index][help] */
127 {
128 char *ptr;
129 ptr=as; ptr++; ptr++;
130 return(atoi(ptr));
131 }
132
133 /* Convert AS range (AS4321 - AS5672) into numbers */
134 int convert_as_range(const char *as_range, int *begin, int *end)
/* [<][>][^][v][top][bottom][index][help] */
135 {
136 char *range;
137 char *token;
138
139 range=g_strdup(as_range);
140 token=range;
141 *begin=convert_as(strsep(&token, " -"));
142 *end=convert_as(strsep(&token, " -"));
143 free(range);
144 return(0);
145 }
146
147 /* Convert time in ASCII format (19991224) into time_t unix time */
148 time_t convert_time(char *asc_time)
/* [<][>][^][v][top][bottom][index][help] */
149 {
150 struct tm tm;
151 char buf[STR_S];
152 char *ptr;
153
154
155 bzero(&tm, sizeof(tm));
156
157 strncpy(buf, asc_time, 4); ptr=buf+4; *ptr='\0';
158 tm.tm_year = atoi(buf) - 1900;
159
160 strncpy(buf, (asc_time+4), 2); ptr=buf+2; *ptr='\0';
161 tm.tm_mon = atoi(buf) - 1;
162
163 strncpy(buf, (asc_time+6), 2); ptr=buf+2; *ptr='\0';
164 tm.tm_mday = atoi(buf);
165
166 return(mktime(&tm));
167
168 }
169
170
171 /************************************************************
172 * char *get_set_name() *
173 * *
174 * Returns set name for the specified object class *
175 * *
176 * **********************************************************/
177 static char *get_set_name(C_Type_t class_type)
/* [<][>][^][v][top][bottom][index][help] */
178 {
179 switch(class_type){
180 case C_RT: return("route_set");
181 case C_AN: return("as_set");
182 case C_IR: return("rtr_set");
183 default: return(NULL);
184 }
185 }
186
187
188 /************************************************************
189 * long get_object_id() *
190 * Queries the database for an object. *
191 * For constructing a query uses each_primary_key_select() *
192 * *
193 * Returns: *
194 * >0 - object exists, returns object_id *
195 * 0 - object does not exist *
196 * -1 - error (f.e. more than one object with the same PK) *
197 * Error code is stored in tr->error *
198 * *
199 * **********************************************************/
200 long get_object_id(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
201 {
202 Object_t *obj;
203 SQ_result_set_t *sql_result;
204 SQ_row_t *sql_row;
205 char *sql_str;
206 long object_id=0;
207 int sql_err;
208
209 obj=tr->object;
210
211 if ((tr->query = g_string_sized_new(STR_XL)) == NULL){
212 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
213 tr->succeeded=0;
214 tr->error |= ERROR_U_MEM;
215 die;
216 }
217
218 /* compose query */
219 g_string_sprintf(tr->query, "SELECT object_id FROM %s WHERE",DF_get_class_sql_table(obj->type));
220 /* add all primary keys */
221 g_slist_foreach(obj->attributes, each_primary_key_select, tr);
222 /* truncate the last ' AND '*/
223 g_string_truncate(tr->query, (tr->query->len) - 4);
224
225 /* execute query */
226 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, tr->query->str);
227 sql_err=SQ_execute_query(tr->sql_connection, tr->query->str, &sql_result);
228
229 /* in case of an error copy error code and return */
230 if(sql_err) {
231 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), tr->query->str);
232 tr->succeeded=0;
233 tr->error |= ERROR_U_DBS;
234 die;
235 }
236 g_string_free(tr->query, TRUE);
237
238 /* Fetch the row */
239 if ((sql_row = SQ_row_next(sql_result)) != NULL) {
240 /* Object exists */
241 #define OBJECT_ID 0
242 sql_str = SQ_get_column_string(sql_result, sql_row, OBJECT_ID);
243 if (sql_str != NULL) {
244 object_id = atol(sql_str);
245 free(sql_str);
246 }
247
248 /* We must process all the rows of the result */
249 /* otherwise we'll have them as part of the next qry */
250 while ( (sql_row = SQ_row_next(sql_result)) != NULL) object_id=-1;
251 } else
252 object_id=0; /* object does not exist*/
253
254 SQ_free_result(sql_result);
255 return(object_id);
256 }
257
258 /************************************************************
259 * get_minmax_id() *
260 * *
261 * Returns the min or max ID of the table *
262 * *
263 * Returns: *
264 * min (max=0) or max (max=1) ID *
265 * -1 in case of an error *
266 * *
267 * *
268 *************************************************************/
269 long get_minmax_id(SQ_connection_t *sql_connection, char *id_name, char *tbl_name, int max)
/* [<][>][^][v][top][bottom][index][help] */
270 {
271 char query[STR_M];
272 SQ_result_set_t *sql_result;
273 SQ_row_t *sql_row;
274 char *sql_str;
275 long id;
276 char *minmax;
277 int sql_err;
278
279 if(max==1)minmax="max"; else minmax="min";
280
281 sprintf(query, "SELECT %s(%s) FROM %s ", minmax, id_name, tbl_name);
282
283 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
284 sql_err = SQ_execute_query(sql_connection, query, &sql_result);
285
286 if(sql_err) {
287 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(sql_connection), query);
288 die;
289 }
290
291
292 if ((sql_row = SQ_row_next(sql_result)) != NULL) {
293 sql_str = SQ_get_column_string(sql_result, sql_row, 0);
294
295 /* We must process all the rows of the result,*/
296 /* otherwise we'll have them as part of the next qry */
297 while ( (sql_row = SQ_row_next(sql_result)) != NULL) {
298 ER_perror(FAC_UD, UD_SQL, "duplicate PK [%s]\n", query);
299 if(sql_str)free(sql_str); sql_str=NULL;
300 die;
301 }
302 }
303 else sql_str=NULL;
304
305 if(sql_result){ SQ_free_result(sql_result); sql_result=NULL; }
306
307 if(sql_str) {
308 id = atol(sql_str);
309 free(sql_str);
310 }
311 else id=-1;
312
313 return(id);
314
315 }
316
317
318 /************************************************************
319 * get_qresult_str() *
320 * *
321 * Returns string containing query result *
322 * *
323 * *
324 * Returns: *
325 * String containing the result.Needs to be freed after use *
326 * NULL in case of an error *
327 * - SQL error *
328 * - if query returns more than one string (row) *
329 * *
330 *************************************************************/
331 char *get_qresult_str(SQ_connection_t *sql_connection, char *query)
/* [<][>][^][v][top][bottom][index][help] */
332 {
333 SQ_result_set_t *sql_result;
334 SQ_row_t *sql_row;
335 char *sql_str;
336 int sql_err;
337
338
339 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
340 sql_err=SQ_execute_query(sql_connection, query, &sql_result);
341
342 if(sql_err) {
343 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(sql_connection), query);
344 die;
345 }
346
347
348 if ((sql_row = SQ_row_next(sql_result)) != NULL) {
349 sql_str = SQ_get_column_string(sql_result, sql_row, 0);
350
351 /* We must process all the rows of the result,*/
352 /* otherwise we'll have them as part of the next qry */
353 while ( (sql_row = SQ_row_next(sql_result)) != NULL) {
354 ER_perror(FAC_UD, UD_SQL, "duplicate PK [%s]\n", query);
355 if(sql_str)free(sql_str); sql_str=NULL;
356 }
357 }
358 else sql_str=NULL;
359
360 SQ_free_result(sql_result);
361 return(sql_str);
362 }
363
364
365
366 /************************************************************
367 * get_field_str() *
368 * *
369 * Returns string containing the field. *
370 * field - field name to be retrieved *
371 * ref_tbl_name - name of the table containing the field *
372 * ref_name - reference name *
373 * attr_value - reference value *
374 * condition - additional condition ( f.e. 'AND dummy=0' *
375 * *
376 * Returns: *
377 * String containing the field. Needs to be freed after use *
378 * NULL in case of an error *
379 * *
380 *************************************************************/
381 char *get_field_str(SQ_connection_t *sql_connection, char *field,
/* [<][>][^][v][top][bottom][index][help] */
382 char *ref_tbl_name, char *ref_name,
383 char * attr_value, char *condition)
384 {
385 char query[STR_L];
386
387 sprintf(query, "SELECT %s FROM %s "
388 "WHERE %s='%s' ",
389 field, ref_tbl_name, ref_name, attr_value);
390 if (condition)strcat(query, condition);
391
392 return( get_qresult_str(sql_connection, query));
393
394 }
395
396 /************************************************************
397 * long get_sequence_id(Transaction_t *tr)
398 * >0 - success
399 * -1 - sql error
400 *
401 * **********************************************************/
402
403 long get_sequence_id(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
404 {
405 char *sql_str;
406 char str_id[STR_M];
407 long sequence_id=-1;
408
409
410 sprintf(str_id, "%ld", tr->object_id);
411 sql_str= get_field_str(tr->sql_connection, "sequence_id", "last", "object_id", str_id, NULL);
412 if(sql_str) {
413 sequence_id = atol(sql_str);
414 free(sql_str);
415 }
416
417 return(sequence_id);
418
419 }
420
421
422 /************************************************************
423 * long get_ref_id(char *ref_tbl_name, char *ref_name, char * attr_value)
424 * >0 - success
425 * -1 - sql error
426 *
427 * **********************************************************/
428
429 static long get_ref_id(Transaction_t *tr, char *ref_tbl_name, char *ref_name, char * attr_value, char *condition)
/* [<][>][^][v][top][bottom][index][help] */
430 {
431 char *sql_str;
432 long ref_id=-1;
433
434 sql_str= get_field_str(tr->sql_connection, "object_id", ref_tbl_name, ref_name, attr_value, condition);
435 if(sql_str) {
436 ref_id = atol(sql_str);
437 free(sql_str);
438 }
439 return(ref_id);
440 }
441
442
443 /************************************************************
444 * int isdummy()
445 *
446 * Returns 1 if the object in question is a dummy,
447 * otherwise returns 0.
448 *
449 * In case of error:
450 * -1 - sql error or object does not exist
451 *
452 ***********************************************************/
453
454 int isdummy(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
455 {
456 char *sql_str;
457 char str_id[STR_M];
458 int object_type=-1;
459
460 sprintf(str_id, "%ld", tr->object_id);
461 sql_str= get_field_str(tr->sql_connection, "object_type", "last", "object_id", str_id, NULL);
462 if(sql_str) {
463 object_type = atoi(sql_str);
464 free(sql_str);
465 }
466
467 if (object_type==-1) {
468 ER_perror(FAC_UD, UD_SQL, "cannot get object type\n");
469 die;
470 }
471 if (object_type==DUMMY_TYPE) return(1);
472 else return(0);
473
474 }
475
476 /* it may be either a legacy name reference, or a nic-handle */
477 /* we rely on other parsers/syntax checkers, so no surprises */
478 /* thus, the check is simple - if there is a space - not a nh */
479 static int isnichandle(char *name)
/* [<][>][^][v][top][bottom][index][help] */
480 {
481 if(index(name, ' ')) return(0);
482 else return(1);
483 }
484
485
486 /************************************************************
487 * process_reverse_domain() *
488 * *
489 * Tries to insert additional data for reverse domains *
490 * This data includes prefix and perfix length for reverse *
491 * delegation block. It is stored in inaddr_arpa table for *
492 * IPv4 and ip6int table for IPv6 address spaces *
493 * *
494 * Returns: *
495 * 0 success *
496 * -1 sql error *
497 * *
498 *************************************************************/
499
500 static int process_reverse_domain(Transaction_t *tr,
/* [<][>][^][v][top][bottom][index][help] */
501 ip_prefix_t *prefptr,
502 int op)
503 {
504 unsigned prefix, prefix_length; /* ipv4 */
505 ip_v6word_t msb, lsb; /* ipv6 */
506 char query[STR_L];
507 int num;
508 int sql_err;
509
510
511 if( IP_pref_b2_space(prefptr) == IP_V4 ) { /* ipv4 */
512 if(op==0) { /* insert record */
513 IP_revd_b2v4(prefptr, &prefix, &prefix_length);
514 sprintf(query, "INSERT INTO inaddr_arpa SET thread_id=%d, object_id=%ld, prefix=%u, prefix_length=%d ",
515 tr->thread_ins, tr->object_id, prefix, prefix_length);
516 }
517 else {
518 /* update record */
519 sprintf(query, "UPDATE inaddr_arpa SET thread_id=%d WHERE object_id=%ld ",
520 tr->thread_upd, tr->object_id);
521 }
522 }
523 else { /* ipv6 */
524 if(op==0) { /* insert record */
525 IP_revd_b2v6(prefptr, &msb, &lsb, &prefix_length);
526 sprintf(query, "INSERT INTO ip6int SET thread_id=%d, object_id=%ld, msb='%llu', lsb='%llu', prefix_length=%d ",
527 tr->thread_ins, tr->object_id, msb, lsb, prefix_length);
528 }
529 else {
530 /* update record */
531 sprintf(query, "UPDATE ip6int SET thread_id=%d WHERE object_id=%ld ",
532 tr->thread_upd, tr->object_id);
533 }
534 }
535
536 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
537 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
538 num = mysql_affected_rows(tr->sql_connection);
539
540 /* Check for errors */
541 if (sql_err) {
542 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query);
543 die;
544 }
545 /* If nothing was affected then WHERE clause returned nothing - DB error */
546 if(num == 0) {
547 ER_perror(FAC_UD, UD_SQL, "insert inaddr had no effect [%s]\n", query);
548 die;
549 }
550 return(0);
551 }
552
553 #define insert_reverse_domain(tr, pr) process_reverse_domain(tr, pr, 0)
/* [<][>][^][v][top][bottom][index][help] */
554 #define update_reverse_domain(tr, pr) process_reverse_domain(tr, pr, 1)
/* [<][>][^][v][top][bottom][index][help] */
555
556
557 /************************************************************
558 * auth_member_of() *
559 * *
560 * Function that checks the authorization for membership *
561 * (i.e. if the object is authorized to be a memeber by *
562 * mbrs-by-ref attribute of the set is refers by member-of *
563 * attribute). *
564 * First checks if 'mbrs-by-ref: ANY' *
565 * If not then checks that maintner referenced by *
566 * mbrs-by-ref attribute of the set is the one in mnt-by. *
567 * *
568 * Returns: *
569 * 0 success *
570 * 1 not allowed *
571 * -1 SQL error *
572 * *
573 *************************************************************/
574 static int auth_member_of(Attribute_t *attr, Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
575 {
576 GString *query;
577 char *set_name;
578 char *qresult;
579
580 /* Check if set has mbrs_by_ref==ANY
581 In such case mbrs_by_ref.mnt_id==0
582 */
583
584 if ((query = g_string_sized_new(STR_XL)) == NULL){
585 tr->succeeded=0;
586 tr->error |= ERROR_U_MEM;
587 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
588 die;
589 }
590
591 set_name = get_set_name(tr->class_type);
592 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s set name retrieved: %s\n", UD_TAG, set_name); */
593
594 /* Check if the set protects itself with mbrs-by-ref attribute */
595 g_string_sprintf(query,"SELECT COUNT(*) FROM mbrs_by_ref, %s "
596 "WHERE mbrs_by_ref.object_id=%s.object_id "
597 "AND %s.%s='%s' ",
598 set_name, set_name, set_name, set_name, attr->value);
599
600 qresult = get_qresult_str(tr->sql_connection, query->str);
601 /* should be '0' if there is no mbrs-by-ref attribute */
602 if (strcmp(qresult, "0")==0){
603 /* there is no mbrs-by-ref attribute - so we cannot go ahead */
604 ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] membership by reference is not allowed (no mbrs-by-ref) [%d:%s]", tr->transaction_id, attr->type, attr->value);
605 g_string_free(query, TRUE);
606 return(1);
607 }
608 else free(qresult);
609
610 /* Check if membership is protected by the keyword "ANY" */
611 /* There is a dummy mntmer object in the database corresponding to "ANY" */
612 /* Its object_id==0 */
613 /* EXAMPLE:
614
615 SELECT route_set.object_id
616 FROM mbrs_by_ref, route_set
617 WHERE mbrs_by_ref.object_id=route_set.object_id
618 AND route_set.route_set=<setname>
619 AND mbrs_by_ref.mnt_id=0
620 */
621 g_string_sprintf(query,"SELECT %s.object_id FROM mbrs_by_ref, %s "
622 "WHERE mbrs_by_ref.object_id=%s.object_id "
623 "AND %s.%s='%s' AND mbrs_by_ref.mnt_id=0 ",
624 set_name, set_name, set_name, set_name, set_name, attr->value);
625
626 qresult = get_qresult_str(tr->sql_connection, query->str);
627 /* if such record exists - go ahead */
628 if(qresult) {
629 free(qresult);
630 g_string_free(query, TRUE);
631 return(0);
632 }
633
634 /* Now check if our mnt_by belongs to mbrs_by_ref list of the set */
635 /* we search only mnt_by.thread_id!=0 to check against new/updated mnt-by attribute */
636 g_string_sprintf(query, "SELECT mbrs_by_ref.object_id FROM %s, mbrs_by_ref, mnt_by "
637 "WHERE mbrs_by_ref.mnt_id=mnt_by.mnt_id "
638 "AND mnt_by.object_id=%ld "
639 "AND %s.object_id=mbrs_by_ref.object_id "
640 "AND %s.%s='%s' "
641 "AND ( mnt_by.thread_id=%d OR mnt_by.thread_id=%d ) ",
642 set_name, tr->object_id, set_name, set_name, set_name, attr->value, tr->thread_upd, tr->thread_ins);
643
644 qresult = get_qresult_str(tr->sql_connection, query->str);
645 /* If our mntner is listed (non-empty result) membership is authorized */
646 if (qresult) {
647 free(qresult);g_string_free(query, TRUE);
648 return(0);
649 } else {
650 ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] membership by reference is not autorized [%d:%s]", tr->transaction_id, attr->type, attr->value);
651 g_string_free(query, TRUE);
652 return(1);
653 }
654 }/* auth_member_of() */
655
656
657 /************************************************************
658 * create_dummy() *
659 * *
660 * Function that creates a dummy object (that is one that *
661 * is referenced from an object but does not *
662 * exist in the database). *
663 * Dummy object exists only in relevant main and 'last' *
664 * tables. Its creation is controlled by tr->dummy_allowed. *
665 * Queries for the dummies are defined in Dummy[] array. *
666 * *
667 * Returns: *
668 * 0 success *
669 * 1 no rf integrity and dummy not allowed *
670 * -1 SQL error *
671 * *
672 *************************************************************/
673 static int create_dummy(Attribute_t *attr, Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
674 {
675 const char *query_fmt;
676 long dummy_id;
677 char query[STR_L];
678 int result=0;
679 char *set_name;
680 char *p_name;
681 int query_type;
682 long timestamp;
683 char str_id[STR_M];
684 gchar *attr_value=NULL;
685 int sql_err;
686 char *token=NULL;
687
688 query_fmt = DF_get_dummy_query(attr->type);
689 if (strcmp(query_fmt, "") == 0) {
690 ER_perror(FAC_UD, UD_BUG, "empty query string\n");
691 die;
692 }
693
694 /* We allow creating dummy sets in any mode */
695 /* For others attributes return if we are in protected mode */
696 if ((attr->type!=A_MO) && (!IS_DUMMY_ALLOWED(tr->mode))) return(1);
697
698 /* Insert dummy in the last table */
699 /* Calculate the object_id - should be max+1 */
700 dummy_id = get_minmax_id(tr->sql_connection, "object_id", "last", 1) +1;
701 /* Record dummy's object_id, it'll be needed in commit/rollback */
702 tr->dummy_id[tr->ndummy]=dummy_id; tr->ndummy++;
703
704 /* Update the TR for crash recovery */
705 /* If we crash before actually creating an entry in last */
706 /* there should be no harm - later in rollback we will just try to delete nonexistent object */
707 TR_update_dummy(tr);
708
709 sprintf(str_id, "%ld", tr->object_id);
710 timestamp=time(NULL);
711 sprintf(query, "INSERT INTO last SET thread_id=%d, timestamp=%ld, object_type=%d, object='DUMMY for %s'",
712 tr->thread_ins, timestamp, DUMMY_TYPE, str_id);
713
714 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
715 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
716
717 /* Check for errors */
718 if (sql_err) {
719 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query);
720 die;
721 }
722
723 /* check that the dummy_id is correct */
724 if(dummy_id != mysql_insert_id(tr->sql_connection)) die; /* probably implementation of autoincrement changed */
725
726
727 /* compose the query */
728 query_type=DF_get_dummy_query_type(attr->type);
729 switch (query_type) {
730
731 /* person_role */
732 case UD_AX_PR:
733 sprintf(query, query_fmt, tr->thread_ins, dummy_id, attr->value, DUMMY_TYPE);
734 break;
735
736 /* maintner */
737 case UD_AX_MT:
738 sprintf(query, query_fmt, tr->thread_ins, dummy_id, attr->value, DUMMY_TYPE);
739 break;
740
741 /* as_set, route_set */
742 case UD_AX_MO:
743 set_name = get_set_name(tr->class_type);
744 sprintf(query, query_fmt, set_name, tr->thread_ins, dummy_id, set_name, attr->value);
745 break;
746
747 default:
748 ER_perror(FAC_UD, UD_BUG, "query not defined for this type of attribute[%d]\n", attr->type);
749 die;
750 break;
751 }
752
753 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
754 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
755 if (sql_err) {
756 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query);
757 die;
758 }
759
760 /* for legacy person/role reference (without nic-handle) create records in names table */
761 if( (query_type == UD_AX_PR) && (!isnichandle (attr->value)) ){
762 /* parse the names */
763 /*ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s adding names for dummy\n", UD_TAG);*/
764 query_fmt = DF_get_insert_query(A_PN);
765 attr_value = g_strdup(attr->value);
766 token = attr_value;
767 while((p_name=strsep(&token, " "))){
768 sprintf(query, query_fmt, tr->thread_ins, dummy_id, DUMMY_TYPE, p_name);
769 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
770 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
771 if (sql_err)
772 if(SQ_errno(tr->sql_connection) != ER_DUP_ENTRY) {
773 ER_perror(FAC_UD, UD_SQL, "insert dummy names:%s[%s]\n", SQ_error(tr->sql_connection), query);
774 result=-1;
775 }
776 }
777 free(attr_value);
778 }
779 return(result);
780 }
781
782 /************************************************************
783 * update_attr() *
784 * *
785 * Function that updates an attribute if it already exists. *
786 * Called from each_attribute_proces() function if it *
787 * cannot insert the row. *
788 * Queries for the attributes are defined in Update[] array. *
789 * *
790 * Returns: Nothing. Error code is stored in tr->error. *
791 * *
792 *************************************************************/
793 static void update_attr(Attribute_t *attr, Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
794 {
795 int num;
796 const char *query_fmt;
797 char *set_name;
798 unsigned int if_address;
799 char * rf_host;
800 int rf_port, rf_type;
801 char *a_value;
802 int sq_info[3];
803 char * condition;
804 char *sq_error;
805 char query[STR_XL];
806 ip_prefix_t dn_pref;
807 int sql_err;
808 char *token;
809 char *mu_mntner;
810
811
812 /* It may be needed to update second attribute stored in the main table, like inetnum, filter-set, etc. */
813 if((tr->load_pass!=0)&&(DF_get_update_query_type(attr->type)!=UD_MA_U2)) return;
814
815 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s updating attribute...\n", UD_TAG);*/
816
817 /* Do some additional processing for reverse domains */
818 /* XXX Later we will implement this under UD_MA_DN case */
819 if ((attr->type == A_DN) && (IP_revd_a2b(&dn_pref, attr->value)==IP_OK)) {
820 if(update_reverse_domain(tr, &dn_pref) !=0 ){
821 tr->error|=ERROR_U_DBS;
822 tr->succeeded=0;
823 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,
824 ERROR_U_DBS, attr->type, attr->value, SQ_error(tr->sql_connection));
825 }
826 }
827
828 /* get query format string */
829 query_fmt = DF_get_update_query(attr->type);
830
831 if (strcmp(query_fmt, "") == 0) return;
832
833 switch (DF_get_update_query_type(attr->type)) {
834 case UD_MAIN_: sprintf(query, query_fmt, tr->thread_upd, tr->object_id);
835 break;
836 case UD_MA_PR:
837 sprintf(query, query_fmt, tr->thread_upd, tr->class_type, tr->object_id);
838 break;
839 case UD_MA_U2: /* save the new value of the attribute for commit*/
840 /* this is necessary for filter(filter-set), netname (inet?num), */
841 /* local-as(inet-rtr) attributes, as they are another field in the record */
842 if((tr->load_pass != 0)){
843 /* for fast loader we need to update the field as we have no commit */
844 sprintf(query, query_fmt, DF_get_class_sql_table(tr->class_type), 0, attr->value, tr->object_id);
845 }
846 else {
847 tr->save=g_strdup(attr->value);
848 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s u2 saved [%s]\n", UD_TAG, tr->save); */
849 /* update TR for crash recovery */
850 TR_update_save(tr);
851 return;
852 }
853 break;
854 case UD_AX_PR:
855 /* This is for non-conformant admin-c, etc.*/
856 a_value=attr->value;
857 if(strlen(attr->value)>MAX_NIC_HDL)*(attr->value + MAX_NIC_HDL)='\0';
858
859 if (!IS_DUMMY_ALLOWED(tr->mode))condition="AND dummy=0 "; else condition=NULL;
860 sprintf(query, query_fmt, tr->thread_upd, tr->object_id,
861 get_ref_id(tr, "person_role", "nic_hdl", attr->value, condition));
862 break;
863 case UD_AX_MT:
864 if (!IS_DUMMY_ALLOWED(tr->mode))condition="AND dummy=0 "; else condition=NULL;
865 sprintf(query, query_fmt, tr->thread_upd, tr->object_id,
866 get_ref_id(tr, "mntner", "mntner", attr->value, condition));
867 break;
868 case UD_AX_MU: /* for mnt_routes table*/
869 a_value=g_strdup(attr->value);
870 token = a_value;
871 mu_mntner=strsep(&token, " ");
872 if (!IS_DUMMY_ALLOWED(tr->mode))condition="AND dummy=0 "; else condition=NULL;
873 sprintf(query, query_fmt, tr->thread_upd, tr->object_id,
874 get_ref_id(tr, "mntner", "mntner", mu_mntner, condition));
875 free(a_value);
876 break;
877 case UD_AX_MO:
878 set_name = get_set_name(tr->class_type);
879 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s retrieved set name: %s\n", UD_TAG, set_name);*/
880 if (!IS_DUMMY_ALLOWED(tr->mode))condition="AND dummy=0 "; else condition=NULL;
881 sprintf(query, query_fmt, tr->thread_upd, tr->object_id,
882 get_ref_id(tr, set_name, set_name, attr->value, condition));
883 break;
884 case UD_AX_MR:
885 if ((strcmp(attr->value, "ANY")==0) || (strcmp(attr->value, "any")==0) || (strcmp(attr->value, "Any")==0))
886 sprintf(query, query_fmt, tr->thread_upd, tr->object_id,
887 get_ref_id(tr, "mntner", "mntner", "ANY",NULL));
888 else {
889 if (!IS_DUMMY_ALLOWED(tr->mode))condition="AND dummy=0 "; else condition=NULL;
890 sprintf(query, query_fmt, tr->thread_upd, tr->object_id,
891 get_ref_id(tr, "mntner", "mntner", attr->value, condition));
892 }
893 break;
894 case UD_LEAF_:
895 sprintf(query, query_fmt, tr->thread_upd, tr->object_id, attr->value);
896 break;
897 case UD_LF_IF:
898 /* Convert ascii ip -> numeric one */
899 convert_if(attr->value, &if_address);
900 sprintf(query, query_fmt, tr->thread_upd, tr->object_id, if_address);
901 break;
902 case UD_LF_RF:
903 rf_host=convert_rf(attr->value, &rf_type, &rf_port);
904 sprintf(query, query_fmt, tr->thread_upd, tr->object_id, rf_type, rf_host, rf_port);
905 if(rf_host)free(rf_host);
906 break;
907 case UD_LF_AY:
908 sprintf(query, query_fmt, tr->thread_upd, tr->object_id, convert_time(attr->value));
909 break;
910 default:
911 tr->error|=ERROR_U_BUG;
912 tr->succeeded=0;
913 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:no update qry\n" ,ERROR_U_BUG, attr->type, attr->value);
914 ER_perror(FAC_UD, UD_BUG, "query not defined for this type of attribute:[%d:%s]\n", attr->type, attr->value);
915 die;
916 break;
917 }
918 /* Execute the query */
919 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
920 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
921 if(sql_err) { /* an error occured*/
922 /* Error - copy the error condition and return */
923 sq_error=SQ_error(tr->sql_connection);
924 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", sq_error, query);
925 tr->error|=ERROR_U_DBS;
926 tr->succeeded=0;
927 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,ERROR_U_DBS, attr->type, attr->value, sq_error);
928 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query);
929 die;
930 }
931 else {
932 /* Query OK */
933 num = mysql_affected_rows(tr->sql_connection);
934 if(num == 0) { /* check for duplicates*/
935 SQ_get_info(tr->sql_connection, sq_info); /* UPDATE ... SET*/
936 if ((sq_info[SQL_DUPLICATES]==0) && (sq_info[SQL_MATCHES]==0)) {
937 /* Condition with zero duplicates and matches may occur when the object is a dummy */
938 /* and we are running in protected mode ( dummies are not allowed, tr->dummy==0). */
939 /* In such case we will append "AND dummy=0" to the query, which won't */
940 /* return a match if the object in question is a dummy */
941 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] dummy prevents update: [%s]", tr->transaction_id, query);
942 tr->error|=ERROR_U_OBJ;
943 tr->succeeded=0;
944 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:dummy update\n" ,ERROR_U_OBJ, attr->type, attr->value);
945 } /* else duplicate entry - silently drop it */
946 }
947 /* For member_of attribute we need to check membership claim in protected mode */
948 if ((attr->type == A_MO) && (!IS_DUMMY_ALLOWED(tr->mode))){
949 if(auth_member_of(attr, tr)!=0){
950 tr->error|=ERROR_U_AUT;
951 tr->succeeded=0;
952 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] membership by reference is not allowed [%d:%s]", tr->transaction_id, attr->type, attr->value);
953 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:membership not allowed\n" ,ERROR_U_AUT, attr->type, attr->value);
954 }
955 }
956 }
957 return;
958 }/* update_attr() */
959
960
961 /************************************************************
962 * each_attribute_proces() *
963 * *
964 * Main function that processes object attributes one by one.*
965 * Called from g_slist_foreach() function. *
966 * First it tries to insert an attribute. *
967 * If an error it assumes that attribute is already in *
968 * a table and calls update_attr() to update it. *
969 * Queries for the attributes are defined in Insert[] array. *
970 * *
971 * Returns: Nothing. Error code is stored in tr->error. *
972 * *
973 *************************************************************/
974 static void each_attribute_process(void *element_data, void *tr_ptr)
/* [<][>][^][v][top][bottom][index][help] */
975 {
976 int num;
977 const char *query_fmt;
978 int query_type;
979 int do_query;
980 Attribute_t *attr = element_data;
981 Transaction_t *tr = (Transaction_t *)tr_ptr;
982 unsigned int prefix, prefix_length, if_address;
983 unsigned int begin_in, end_in;
984 ip_v6word_t high, low;
985
986 int begin_as, end_as;
987 char query[STR_XL];
988 char * set_name;
989 char * rf_host; /* needs to be freed after use*/
990 int rf_type, rf_port;
991 char *a_value;
992 int sq_info[3];
993 char *mu_mntner, *mu_prefix;
994 int dummy_err;
995 char *sq_error;
996 ip_prefix_t dn_pref;
997 int sql_err;
998 int res;
999 char *token;
1000
1001 /* we still want to continue to collect all possible errors*/
1002 /* if(tr->succeeded == 0) return; */
1003
1004 /* To switch off querying for some types of attributes */
1005 do_query=1;
1006
1007 /* Determine the query type */
1008 query_type=DF_get_insert_query_type(attr->type);
1009
1010 /* For loadind pass #1 we need to process only main tables */
1011 if(tr->load_pass==1){
1012 switch(query_type) {
1013 case UD_MAIN_:
1014 case UD_MA_U2:
1015 case UD_MA_PR:
1016 case UD_MA_RT:
1017 case UD_MA_IN:
1018 case UD_MA_I6:
1019 case UD_MA_OR:
1020 case UD_MA_AK:
1021 break;
1022 default: return; /* return for other than MAIN tables*/
1023 }
1024 }
1025
1026 query_fmt = DF_get_insert_query(attr->type);
1027
1028 /* return if no query is defined for this attribute */
1029 if (strcmp(query_fmt, "") == 0) return;
1030
1031 /* compose the query depending on the attribute */
1032 switch (query_type) {
1033 case UD_MAIN_: /* for MAIN tables */
1034 if (ACT_UPDATE(tr->action)) do_query=0;
1035 else
1036 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, attr->value);
1037 break;
1038 case UD_MA_OR: /* for the origin attribute */
1039 if (ACT_UPDATE(tr->action)) do_query=0;
1040 else {
1041 sprintf(query, query_fmt, tr->thread_ins, attr->value, tr->object_id);
1042 tr->action |= TA_UPD_RX;
1043 RP_pack_set_orig(attr->type, tr->packptr, attr->value);
1044 }
1045 break;
1046 case UD_MA_PR: /* for person_role table*/
1047 if (ACT_UPDATE(tr->action)) do_query=0;
1048 else
1049 sprintf(query, query_fmt, tr->thread_ins, tr->class_type, tr->object_id, attr->value);
1050
1051 /* check if we need to update NHR */
1052 if (ACT_UPD_NHR(tr->action)) {
1053 /* Check if we can allocate it */
1054 res = NH_check(tr->nh, tr->sql_connection);
1055 if(res == -1) { /* we cannot allocate this NIC handle (DB error) */
1056 tr->succeeded=0;
1057 tr->error |= ERROR_U_DBS;
1058 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:cannot allocate nic-handle\n", ERROR_U_DBS, attr->type, attr->value);
1059 ER_perror(FAC_UD, UD_SQL, "cannot allocate nic hdl[%s]\n", attr->value);
1060 die;
1061 }
1062 else
1063 if(res == 0) { /* we cannot allocate this NIC handle (full space or ID in use) */
1064 tr->succeeded=0;
1065 tr->error |= ERROR_U_OBJ;
1066 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:nic-handle already in use\n", ERROR_U_OBJ, attr->type, attr->value);
1067 return;
1068 }
1069 }
1070 break;
1071 case UD_MA_RT: /* for route table*/
1072 if (ACT_UPDATE(tr->action)) do_query=0;
1073 else {
1074 tr->action |= TA_UPD_RX;
1075 RP_pack_set_pref4(attr->type, attr->value, tr->packptr, &prefix, &prefix_length);
1076 /*ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s route: %u/%u\n", UD_TAG, prefix, prefix_length); */
1077 sprintf(query, query_fmt, tr->thread_ins,
1078 tr->object_id, prefix, prefix_length);
1079 }
1080 break;
1081 case UD_MA_IN: /* for inetnum table*/
1082 if (ACT_UPDATE(tr->action)) do_query=0;
1083 else {
1084 tr->action |= TA_UPD_RX;
1085 RP_pack_set_rang(attr->type, attr->value, tr->packptr, &begin_in, &end_in);
1086 /* XXX error handling ? */
1087 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, begin_in, end_in);
1088 }
1089 break;
1090 case UD_MA_I6: /* for inet6num table*/
1091 if (ACT_UPDATE(tr->action)) do_query=0;
1092 else {
1093 tr->action |= TA_UPD_RX;
1094 RP_pack_set_pref6(attr->type, attr->value, tr->packptr, &high, &low, &prefix_length);
1095 /* XXX error handling ? */
1096 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, high, low, prefix_length);
1097 }
1098 break;
1099 case UD_MA_U2: /* This is actually an update - go to update_attr - this is more natural */
1100 do_query=0;
1101 break;
1102 case UD_MA_AK: /* for as_block table*/
1103 if (ACT_UPDATE(tr->action)) do_query=0;
1104 else {
1105 convert_as_range(attr->value, &begin_as, &end_as);
1106 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, begin_as, end_as);
1107 }
1108 break;
1109 case UD_AUX__: /* for AUX tables*/
1110 if((attr->type==A_AC) || (attr->type==A_TC) || (attr->type==A_ZC))
1111 if(strlen(attr->value)>MAX_NIC_HDL)*(attr->value + MAX_NIC_HDL)='\0';
1112
1113 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
1114 if(!IS_DUMMY_ALLOWED(tr->mode))strcat(query, " AND dummy=0 ");
1115 break;
1116 case UD_AX_MO: /* for member_of table*/
1117 set_name = get_set_name(tr->class_type);
1118 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s retrieved set name: %s\n", UD_TAG, set_name);*/
1119 sprintf(query, query_fmt, tr->thread_ins,
1120 tr->object_id, set_name, tr->class_type, set_name, set_name, set_name, attr->value);
1121 break;
1122 case UD_AX_MR: /* for mbrs_by_ref table*/
1123 if ((strcmp(attr->value, "ANY")==0) || (strcmp(attr->value, "any")==0) || (strcmp(attr->value, "Any")==0))
1124 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, "ANY");
1125 else
1126 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
1127 break;
1128 case UD_AX_MU: /* for mnt_routes table*/
1129 a_value=g_strdup(attr->value);
1130 token = a_value;
1131 mu_mntner=strsep(&token, " ");
1132 mu_prefix=token;
1133 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, mu_mntner);
1134 free(a_value);
1135 if (!IS_DUMMY_ALLOWED(tr->mode))strcat(query, " AND dummy=0 ");
1136 break;
1137 case UD_LEAF_: /* for LEAF tables*/
1138 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, attr->value);
1139 break;
1140 case UD_LF_OT: /* for LEAF tables containing object_type field*/
1141 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
1142 break;
1143 case UD_LF_AT: /* check PGPKEY. If yes - check the existence of key-cert.*/
1144 if(!IS_DUMMY_ALLOWED(tr->mode)){
1145 if(strncmp("PGPKEY", attr->value, 6)==0) {
1146 if(get_ref_id(tr, "key_cert", "key_cert", attr->value, NULL)<=0) {
1147 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] no key-cert object[%s]", tr->transaction_id, attr->value);
1148 tr->error|=ERROR_U_OBJ;
1149 tr->succeeded=0;
1150 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:no key-cert object\n" ,ERROR_U_OBJ, attr->type, attr->value);
1151 return;
1152 }
1153 }
1154 }
1155 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, tr->class_type, attr->value);
1156 break;
1157 case UD_LF_IF: /* for ifaddr tables*/
1158 /* Convert ascii ip -> numeric one*/
1159 convert_if(attr->value, &if_address);
1160 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, if_address);
1161 break;
1162 case UD_LF_RF: /* for refer table*/
1163 rf_host=convert_rf(attr->value, &rf_type, &rf_port);
1164 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, rf_type, rf_host, rf_port);
1165 if(rf_host)free(rf_host);
1166 break;
1167 case UD_LF_AY: /* for auth_override table*/
1168 sprintf(query, query_fmt, tr->thread_ins, tr->object_id, convert_time(attr->value));
1169 break;
1170 default:
1171 tr->succeeded=0;
1172 tr->error |= ERROR_U_BUG;
1173 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:query not defined for the attribute\n" ,ERROR_U_BUG, attr->type, attr->value);
1174 ER_perror(FAC_UD, UD_BUG, "query not defined for this type of attribute:[%d:%s]\n", attr->type, attr->value);
1175 die;
1176 break;
1177 }
1178
1179 /* Make the query. For primary keys go straight to updates if we are updating the object */
1180 if(do_query){
1181 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
1182 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
1183 }
1184 else {
1185 update_attr(attr, tr);
1186 return;
1187 }
1188
1189 if (sql_err) {
1190 /* we received an error */
1191 if(SQ_errno(tr->sql_connection) == ER_DUP_ENTRY){ /* Only error "Duplicate entry" may be considered*/
1192 if (ACT_UPDATE(tr->action)) { /* In update mode this is common (so actually not an error)*/
1193 update_attr(attr, tr);
1194 return;
1195 }
1196 /* Otherwise this is a duplicate attribute, just ignore it */
1197 /* In the future if we are more stringent, checks may be added here */
1198 }
1199 else { /* Other errors reveal a database/server problem*/
1200 sq_error=SQ_error(tr->sql_connection);
1201 tr->error|=ERROR_U_DBS;
1202 tr->succeeded=0;
1203 ER_perror(FAC_UD, UD_BUG, "%s[%s]\n", sq_error, query);
1204 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,ERROR_U_DBS, attr->type, attr->value, sq_error);
1205 die;
1206 }
1207 } /* if error occured */
1208 else {
1209 /* If the query was successful */
1210 num = mysql_affected_rows(tr->sql_connection);
1211 if(num>0){ /* this is OK*/
1212 /* Do some additional processing for member_of attribute */
1213 if ((attr->type == A_MO) && (!IS_DUMMY_ALLOWED(tr->mode))){
1214 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s need to auth membership\n", UD_TAG);*/
1215 if(auth_member_of(attr, tr)!=0){
1216 tr->error|=ERROR_U_AUT;
1217 tr->succeeded=0;
1218 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] membership not allowed [%d:%s]", tr->transaction_id, attr->type, attr->value);
1219 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:membership not allowed\n" ,ERROR_U_AUT, attr->type, attr->value);
1220 }
1221 }
1222 else
1223 /* Do some additional processing for reverse zones domains */
1224 if ((attr->type == A_DN)
1225 && IP_revd_a2b(&dn_pref, attr->value)==IP_OK ) {
1226
1227 if(insert_reverse_domain(tr, &dn_pref) != 0 ) {
1228 tr->error|=ERROR_U_DBS;
1229 tr->succeeded=0;
1230 ER_perror(FAC_UD, UD_SQL, "cannot insert inverse domain:[%d:%s]\n", attr->type, attr->value);
1231 die;
1232 }
1233 else {
1234 /* save data for the radix tree update */
1235 tr->action |= TA_UPD_RX;
1236 RP_pack_set_revd(attr->type, attr->value, tr->packptr);
1237 }
1238 }
1239 return;
1240 }
1241 if(num == 0) {
1242 /* this could be an empty update or a null select */
1243 SQ_get_info(tr->sql_connection, sq_info);
1244 if (sq_info[SQL_DUPLICATES]>0) {
1245 /* INSERT ... SELECT ... affected 0 rows, but there is 1 duplicate */
1246 /* which means that we already have such record in the table */
1247 /* this indicates that this is actually an update - update this attribute */
1248 if (sq_info[SQL_DUPLICATES]>1) {
1249 tr->error|=ERROR_U_DBS;
1250 tr->succeeded=0;
1251 ER_perror(FAC_UD, UD_SQL, "too many duplicates:[%d:%s]\n", attr->type, attr->value);
1252 die;
1253 }
1254 update_attr(attr, tr);
1255 }
1256 else {
1257 /* this is an emty SELECT because there is no referred object */
1258 /* try to create dummy and repeat the original query*/
1259
1260 /* ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s no ref. integrity. Trying to create dummy\n", UD_TAG);*/
1261
1262 dummy_err = create_dummy(attr, tr);
1263 if (dummy_err == 0) {
1264 /* Dummy was created */
1265 g_string_sprintfa(tr->error_script,"W[%d][%d:%s]:dummy created\n" ,0, attr->type, attr->value);
1266 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query);
1267 sql_err = SQ_execute_query(tr->sql_connection, query, (SQ_result_set_t **)NULL);
1268 num = mysql_affected_rows(tr->sql_connection);
1269 if (sql_err) {
1270 sq_error=SQ_error(tr->sql_connection);
1271 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", sq_error, query);
1272 tr->error|=ERROR_U_DBS;
1273 tr->succeeded=0;
1274 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:%s\n" ,
1275 ERROR_U_DBS, attr->type, attr->value, sq_error);
1276 die;
1277 }
1278 if (num==0) {
1279 ER_perror(FAC_UD, UD_SQL, "0 rows affected [%s]\n", query);
1280 tr->error|=ERROR_U_DBS;
1281 tr->succeeded=0;
1282 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:re-insert qry\n" ,
1283 ERROR_U_DBS, attr->type, attr->value);
1284 die;
1285 }
1286 }
1287 else
1288 if(dummy_err == 1) {
1289 /* dummy not allowed */
1290 tr->error |= ERROR_U_OBJ;
1291 tr->succeeded=0;
1292 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:dummy not allowed\n" ,ERROR_U_OBJ, attr->type, attr->value);
1293 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] dummy not allowed [%d:%s]", tr->transaction_id, attr->type, attr->value);
1294 }
1295 else {
1296 /* SQL problem */
1297 tr->error|=ERROR_U_DBS;
1298 tr->succeeded=0;
1299 ER_perror(FAC_UD, UD_SQL, "dummy cannot be created [%d:%s]", attr->type, attr->value);
1300 g_string_sprintfa(tr->error_script,"E[%d][%d:%s]:dummy cannot be created\n" ,ERROR_U_DBS, attr->type, attr->value);
1301 die;
1302 }
1303 } /* RI*/
1304 }/* if num == 0*/
1305 } /* if the query was successful */
1306
1307 return;
1308 } /* each_attribute_process() */
1309
1310
1311
1312 /************************************************************
1313 * each_primary_key_select() *
1314 * *
1315 * Function that forms a query for an object (w prinary keys)*
1316 * Called from g_slist_foreach() function. *
1317 * Primary keys are defined in Select[] array. *
1318 * *
1319 * Returns: Nothing. *
1320 * *
1321 *************************************************************/
1322 static void each_primary_key_select(void *element_data, void *result_ptr)
/* [<][>][^][v][top][bottom][index][help] */
1323 {
1324 Attribute_t *attr = element_data;
1325 Transaction_t *tr = (Transaction_t *)result_ptr;
1326 const char *query_fmt;
1327 unsigned int prefix, prefix_length;
1328 unsigned int begin_in, end_in;
1329 int begin_as, end_as;
1330 ip_prefix_t prefstr;
1331 ip_range_t rangstr;
1332 ip_v6word_t i6_msb, i6_lsb;
1333
1334 query_fmt = DF_get_select_query(attr->type);
1335
1336 if (strcmp(query_fmt, "") != 0) {
1337 switch (DF_get_select_query_type(attr->type)) {
1338 case UD_MAIN_:
1339 g_string_sprintfa(tr->query, query_fmt, attr->value);
1340 g_string_sprintfa(tr->K, attr->value);
1341 break;
1342 case UD_MA_RT:
1343 IP_pref_a2v4(attr->value, &prefstr, &prefix, &prefix_length);
1344 g_string_sprintfa(tr->query, query_fmt, prefix, prefix_length);
1345 g_string_sprintfa(tr->K, attr->value);
1346 break;
1347 case UD_MA_IN:
1348 IP_rang_a2v4(attr->value, &rangstr, &begin_in, &end_in);
1349 g_string_sprintfa(tr->query, query_fmt, begin_in, end_in);
1350 g_string_sprintfa(tr->K, attr->value);
1351 break;
1352 case UD_MA_I6:
1353 IP_pref_a2v6(attr->value, &prefstr, &i6_msb, &i6_lsb, &prefix_length);
1354 g_string_sprintfa(tr->query, query_fmt, i6_msb, i6_lsb, prefix_length);
1355 g_string_sprintfa(tr->K, attr->value);
1356 break;
1357 case UD_MA_AK:
1358 convert_as_range(attr->value, &begin_as, &end_as);
1359 g_string_sprintfa(tr->query, query_fmt, begin_as, end_as);
1360 g_string_sprintfa(tr->K, attr->value);
1361 break;
1362 default:
1363 ER_perror(FAC_UD, UD_BUG, "query not defined for this type of attribute:[%d:%s]\n", attr->type, attr->value);
1364 die;
1365
1366 break;
1367 }
1368 }
1369 }
1370
1371 /************************************************************
1372 * perform_create(const Object_t *obj, Transaction_t *tr) *
1373 * *
1374 * Procedure for creating a new object. *
1375 * First inserts object into 'last' table and gets object_id.*
1376 * Then processes all attributes. *
1377 * *
1378 * Returns: tr->succeeded: >0 success, 0 - error *
1379 * Error code is stored in tr->error. *
1380 * *
1381 *************************************************************/
1382 static int perform_create(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
1383 {
1384 Object_t *obj;
1385 char *str;
1386 GString *query;
1387 long timestamp;
1388 int sql_err;
1389 long object_id;
1390
1391
1392 if ((query = g_string_sized_new(STR_XL)) == NULL){
1393 tr->succeeded=0;
1394 tr->error |= ERROR_U_MEM;
1395 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
1396 die;
1397 }
1398
1399
1400 obj=tr->object;
1401
1402 str = (obj->object)->str;
1403 timestamp=time(NULL);
1404 tr->sequence_id=1; /* we start with 1*/
1405 /* Calculate the object_id - should be max+1 */
1406 tr->object_id = get_minmax_id(tr->sql_connection, "object_id", "last", 1);
1407 /* if last is empty, start with 1, otherwise the assign the next id */
1408 if(tr->object_id==-1)tr->object_id=1; else tr->object_id++;
1409 TR_update_id(tr);
1410
1411 g_string_sprintf(query, "INSERT INTO last SET thread_id=%d, timestamp=%ld, sequence_id=1, object_type=%d, object='%s', pkey='%s' ",
1412 tr->thread_ins, timestamp, tr->class_type, str, tr->K->str);
1413
1414 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query->str);
1415 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
1416
1417 /* Check for affected rows. One row should be affected . */
1418 if (sql_err) {
1419 tr->error|=ERROR_U_DBS;
1420 tr->succeeded=0;
1421 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
1422 die;
1423 }
1424 else {
1425 /* Get generated (autoincrement) object_id */
1426 object_id=mysql_insert_id(tr->sql_connection);
1427 /* compare it with the calculated one */
1428 if(tr->object_id != object_id) die; /* probably implementation of autoincrement changed */
1429 g_slist_foreach(obj->attributes, each_attribute_process, tr);
1430 }
1431 g_string_free(query, TRUE);
1432 return(tr->succeeded);
1433 } /* perform_create() */
1434
1435 /************************************************************
1436 * perform_update(Transaction_t *tr) *
1437 * *
1438 * Procedure for updating (existing) object. *
1439 * First processes all attributes. *
1440 * Then saves previous object in 'history' and updates *
1441 * 'last' table. *
1442 * *
1443 * Returns: tr->succeeded: >0 success, 0 - error *
1444 * Error code is stored in tr->error. *
1445 * *
1446 *************************************************************/
1447 static int perform_update(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
1448 {
1449 Object_t *obj;
1450 char *str;
1451 GString *query;
1452 int num;
1453 long sequence_id;
1454 long timestamp;
1455 char *sq_error;
1456 int sql_err;
1457
1458
1459 obj=tr->object;
1460 /* get sequence number */
1461
1462 sequence_id = get_sequence_id(tr);
1463 if(sequence_id==-1) {
1464 tr->error|=ERROR_U_DBS;
1465 tr->succeeded=0;
1466 ER_perror(FAC_UD, UD_SQL, "cannot get sequence_id");
1467 die;
1468 }
1469 else tr->sequence_id=sequence_id; /* save it for rollback*/
1470 /* Update TR record */
1471 TR_update_id(tr);
1472
1473 /* process each attribute one by one */
1474 g_slist_foreach(obj->attributes, each_attribute_process, tr);
1475
1476 /* If we've already failed or this is fast load - just return */
1477 if((tr->succeeded == 0) || (tr->load_pass != 0)) return(tr->succeeded);
1478
1479 /* No return: thread_id=0 */
1480 /* Do it only if previous transactions finished well */
1481 if ((query = g_string_sized_new(STR_XL)) == NULL){
1482 tr->succeeded=0;
1483 tr->error |= ERROR_U_MEM;
1484 ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring");
1485 die;
1486 }
1487 /* copy object to the history table */
1488 g_string_sprintf(query,"INSERT history "
1489 "SELECT %d, object_id, sequence_id, timestamp, object_type, object, pkey, serial, prev_serial "
1490 "FROM last "
1491 "WHERE object_id=%ld ", tr->thread_ins, tr->object_id);
1492
1493 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query->str);
1494 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
1495
1496 /* Check for affected rows. One row should be affected . */
1497 num = mysql_affected_rows(tr->sql_connection);
1498 if (num < 1) {
1499 tr->error|=ERROR_U_DBS;
1500 tr->succeeded=0;
1501 if (sql_err) {
1502 sq_error=SQ_error(tr->sql_connection);
1503 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
1504 die;
1505 }
1506 else {
1507 ER_perror(FAC_UD, UD_SQL, "0 rows affected [%s]\n", query->str);
1508 /* This is to check that this is really could happen */
1509 die;
1510 }
1511 g_string_free(query, TRUE);
1512 return(tr->succeeded);
1513 }
1514
1515 /* Insert new version into the last */
1516
1517 /* Put a timestamp */
1518 str = (obj->object)->str;
1519 timestamp=time(NULL);
1520
1521 /* update last for commit/rollback */
1522
1523 g_string_sprintf(query, "INSERT last "
1524 "SET thread_id=%d, object_id=%ld, sequence_id=%ld, timestamp=%ld, object_type=%d, object='%s', pkey='%s' ",
1525 tr->thread_ins, tr->object_id, tr->sequence_id+1, timestamp, tr->class_type, str, tr->K->str);
1526
1527
1528 ER_dbg_va(FAC_UD, ASP_UD_SQL, "%s [%s]", UD_TAG, query->str);
1529 sql_err = SQ_execute_query(tr->sql_connection, query->str, (SQ_result_set_t **)NULL);
1530
1531 /* Check for affected rows. One row should be affected */
1532 num = mysql_affected_rows(tr->sql_connection);
1533 if (num < 1) {
1534 tr->error|=ERROR_U_DBS;
1535 tr->succeeded=0;
1536 if(sql_err) {
1537 g_string_sprintfa(tr->error_script,"E[%d][:]:UPDATE last failed:%s\n" ,ERROR_U_DBS,SQ_error(tr->sql_connection));
1538 ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
1539 die;
1540 }
1541 else {
1542 ER_perror(FAC_UD, UD_SQL, "0 rows affected [%s]\n", query->str);
1543 /* This is to check that this is really could happen */
1544 die;
1545 }
1546 g_string_free(query, TRUE);
1547 return(tr->succeeded);
1548 }
1549 g_string_free(query, TRUE);
1550 return(tr->succeeded);
1551 } /* perform_update() */
1552
1553
1554
1555
1556 /************************************************************
1557 * int object_process(Transaction_t *tr) *
1558 * *
1559 * This is the interface between core and upper layer *
1560 * All it gets is Transaction *tr, which contains all *
1561 * necessary information, including the object in its *
1562 * internal representation. *
1563 * *
1564 * Returns: tr->succeeded: >0 success, 0 - error *
1565 * Error code is stored in tr->error. *
1566 * *
1567 *************************************************************/
1568 int object_process(Transaction_t *tr)
/* [<][>][^][v][top][bottom][index][help] */
1569 {
1570 int res;
1571 char nic[MAX_NH_LENGTH];
1572 int commit_now;
1573
1574 /* for fast loader we do not perform commits/rollbacks */
1575 if(tr->load_pass == 0) commit_now = 0; else commit_now = 1;
1576
1577 /* create and initialize TR record for crash recovery */
1578 TR_create_record(tr);
1579
1580 if(ACT_DELETE(tr->action)){
1581 ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s object: delete", UD_TAG);
1582 /* check referential integrity of deletion */
1583 UD_check_ref(tr);
1584 /* for person & role - free the nic-handle in the NHR */
1585 if(tr->succeeded && ((tr->class_type==C_PN) || (tr->class_type==C_RO)) && tr->nh ){
1586 res = NH_free(tr->nh, tr->sql_connection, commit_now);
1587
1588 if(res == -1) {
1589 tr->succeeded=0;
1590 tr->error |= ERROR_U_DBS;
1591 ER_perror(FAC_UD, UD_SQL, "cannot delete nic handle");
1592 die;
1593 }
1594 else if(res == 0) {
1595 tr->succeeded=0;
1596 tr->error |= ERROR_U_OBJ;
1597 ER_perror(FAC_UD, UD_SQL, "nic handle not found");
1598 die;
1599 }
1600 }
1601 /* if everything is Ok we are ready to commit */
1602 if (tr->succeeded){
1603 /* update object_id and sequence_id fields */
1604 tr->sequence_id = get_sequence_id(tr);
1605 TR_update_id(tr);
1606
1607 /* checkpoint the TR - we are going to commit*/
1608 CP_COMMIT(tr->action); TR_update_escript(tr); TR_update_status(tr);
1609
1610 /* send an ack */
1611 UD_ack(tr);
1612
1613 /* delete the object and checkpoint it*/
1614 UD_delete(tr);
1615 UD_update_rx(tr, RX_OPER_DEL);
1616
1617 /* we need to update sequence_id because it was changed during update */
1618 CP_DELETE_PASSED(tr->action); TR_update_id(tr); TR_update_status(tr);
1619
1620 /* Commit nic-handle deletion to the repository */
1621 NH_commit(tr->sql_connection);
1622
1623 CP_COMMIT_NH_PASSED(tr->action); TR_update_status(tr);
1624
1625 }
1626 else { /* just send an ack */
1627 UD_ack(tr);
1628 }
1629 return(tr->succeeded); /*commit is not needed*/
1630 }
1631 else if(ACT_UPDATE(tr->action)){
1632 ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "%s object: update\n", UD_TAG);
1633 perform_update(tr);
1634
1635 /* Commit nic-handle allocation (if any) to the repository if we are replacing dummy*/
1636 if(ACT_UPD_NHR(tr->action) && tr->succeeded && ((tr->class_type==C_PN) || (tr->class_type==C_RO)) && tr->nh ){
1637 /* convert nh to DB nIC handle before registration */
1638 /* because there nh will bee freed */
1639 NH_convert(nic, tr->nh);
1640
1641 res = NH_register(tr->nh, tr->sql_connection, commit_now);
1642
1643 if(res == -1) {
1644 tr->succeeded=0;
1645 tr->error |= ERROR_U_DBS;
1646 ER_perror(FAC_UD, UD_SQL, "cannot allocate nic handle\n");
1647 die;
1648 }
1649 else if(res == 0) {
1650 tr->succeeded=0;
1651 tr->error |= ERROR_U_OBJ;
1652 ER_perror(FAC_UD, UD_SQL, "nic handle already in use\n");
1653 die;
1654 }
1655 else { /* copy the NH to the report to return to DBupdate */
1656 /* Convert nh to the database format */
1657 g_string_sprintfa(tr->error_script,"I[%d][%s]\n", A_NH, nic);
1658 }
1659 }
1660 }
1661 else if(ACT_CREATE(tr->action)){
1662 ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s object: create", UD_TAG);
1663 perform_create(tr);
1664
1665 /* Commit nic-handle allocation (if any) to the repository */
1666 if(tr->succeeded && ((tr->class_type==C_PN) || (tr->class_type==C_RO)) && tr->nh ){
1667 /* convert nh to DB nIC handle before registration */
1668 NH_convert(nic, tr->nh);
1669
1670 res = NH_register(tr->nh, tr->sql_connection, commit_now);
1671
1672 if(res == -1) {
1673 tr->succeeded=0;
1674 tr->error |= ERROR_U_DBS;
1675 ER_perror(FAC_UD, UD_SQL, "cannot allocate nic handle");
1676 die;
1677 }
1678 else if(res == 0) {
1679 tr->succeeded=0;
1680 tr->error |= ERROR_U_OBJ;
1681 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] object: nic handle %s already in use", tr->transaction_id, nic);
1682 g_string_sprintfa(tr->error_script,"E[%d][nic handle %s already in use]\n", A_NH, nic);
1683 }
1684 else { /* copy the NH to the report to return to DBupdate */
1685 g_string_sprintfa(tr->error_script,"I[%d][%s]\n", A_NH, nic);
1686 }
1687 }
1688
1689 }
1690 else {
1691 ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] object: unknown action", tr->transaction_id);
1692 tr->succeeded=0;
1693 tr->error|=ERROR_U_BADOP;
1694 return(tr->succeeded);
1695 }
1696
1697 if(tr->load_pass == 0) { /* not for fast loader*/
1698 /* update object_id and sequence_id fields */
1699 TR_update_id(tr);
1700
1701 if (tr->succeeded) {
1702 /*ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s Commit transaction\n", UD_TAG); */
1703 /* checkpoint the TR - we are going to commit*/
1704 CP_COMMIT(tr->action); TR_update_escript(tr); TR_update_status(tr);
1705
1706 /* send an ack */
1707 UD_ack(tr);
1708 /* commit the transaction and checkpoint it */
1709
1710 UD_commit(tr);
1711 /* Commit nic-handle modifications to the repository */
1712
1713 NH_commit(tr->sql_connection);
1714
1715 CP_COMMIT_NH_PASSED(tr->action); TR_update_status(tr);
1716 /* TR will be marked as clean in UD_create_serial() */
1717 }
1718 else {
1719 /*ER_dbg_va(FAC_UD, ASP_UD_OBJ, "%s roll back transaction\n", UD_TAG); */
1720 /* send an ack */
1721 UD_ack(tr);
1722 UD_rollback(tr);
1723
1724 CP_ROLLBACK_PASSED(tr->action); TR_update_status(tr);
1725
1726 /* rollback nic-handle modifications to the repository */
1727 NH_rollback(tr->sql_connection);
1728
1729
1730 CP_ROLLBACK_NH_PASSED(tr->action); TR_update_status(tr);
1731 /* Delete TR record if in update mode. Next time (if any) DBupdate tries to submit, we'll start from scratch */
1732 /* In NRTM mode we create serial anyway, so the record will be deleted */
1733 /* after serial is created TR record will be deleted in */
1734
1735 }
1736 }
1737 return(tr->succeeded);
1738 } /* object_process() */
1739