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