modules/nh/nh.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- get_min_range
- NH_convert
- NH_parse
- NH_check
- NH_free
- NH_register
- free_nh
- get_range
- update_range
- create_range
1 #include <glib.h>
2 #include <stdio.h>
3 #include <strings.h>
4 #include <glib.h>
5 #include <stdlib.h>
6 #include <ctype.h>
7 #include <unistd.h>
8
9 #include "nh.h"
10 #include <stubs.h>
11
12 /*+ String sizes +*/
13 #define STR_S 63
14 #define STR_M 255
15 #define STR_L 1023
16 #define STR_XL 4095
17 #define STR_XXL 16383
18 #define STR_XXXL 65535
19
20
21
22 #define get_min_range(prange, sql_connection) get_range(-1, prange, sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
23 static long get_range(long nic_id, range_t *prange, SQ_connection_t *sql_connection);
24 static long update_range(long range_id, range_t *p_newrange, SQ_connection_t *sql_connection);
25 static long create_range(range_t *p_range, SQ_connection_t *sql_connection);
26
27 /************************************************************
28 * int NH_convert() *
29 * *
30 * Converts space & nic_id into a database nic-handle *
31 * *
32 * *
33 * Returns: *
34 * The size of the nic_handle in characters *
35 * *
36 ************************************************************/
37 int NH_convert(char *nic, nic_handle_t *nh_ptr)
/* [<][>][^][v][top][bottom][index][help] */
38 {
39 /* Check for special cases */
40 /* Is is and AUTO nic-handle ? */
41 if(nh_ptr->nic_id == AUTO_NIC_ID) return(-1);
42 if(nh_ptr->space) nic+=sprintf(nic, "%s", nh_ptr->space);
43 /* No nic-id ? */
44 if(nh_ptr->nic_id != NULL_NIC_ID) nic+=sprintf(nic, "%ld", nh_ptr->nic_id);
45 /* No source ? */
46 if (nh_ptr->source) sprintf(nic, "%s", nh_ptr->source);
47 return(1);
48 }
49
50 /************************************************************
51 * int NH_parse() *
52 * *
53 * Parse a nic handle as supplied by DBupdate *
54 * The format is: <space>[<nic_id>|*][SOURCE] *
55 * Also extracts nic_id and space for regular nic-handles *
56 * *
57 * *
58 * Returns: *
59 * >0 - success *
60 * 0 - AUTO NIC *
61 * -1 - error (not defined and processed yet) *
62 * *
63 ************************************************************/
64 int NH_parse(char *nic, nic_handle_t **nh_ptr_ptr)
/* [<][>][^][v][top][bottom][index][help] */
65 {
66 char *ptr;
67 int res = 1;
68 nic_handle_t *nh_ptr;
69
70 if(!(nh_ptr=calloc(1, sizeof(nic_handle_t)))) die;
71
72 ptr=nic;
73
74 /* extract space */
75 while(isalpha((int)*ptr))ptr++;
76 if(!(nh_ptr->space=malloc(ptr-nic+1))) die;
77 strncpy(nh_ptr->space, nic, ptr-nic); *(nh_ptr->space+(ptr-nic))='\0';
78
79 /* If there are no digits, then this is no nic-hdl */
80 /* We reserve NULL_NIC_ID for such pretty identifiers */
81 if(*ptr == '\0') {
82 nh_ptr->nic_id=NULL_NIC_ID;
83 nh_ptr->source=NULL;
84 }
85 else {
86 /* Check if it is and AUTO nic */
87 if (*ptr == '*') {
88 /* For AUTO nic_id we reserve AUTO_NIC_ID */
89 nh_ptr->nic_id=AUTO_NIC_ID;
90 res=0;
91 ptr++;
92 } else {
93 nic=ptr;
94 /* convert digits (if any) and store first invalid characted in ptr */
95 nh_ptr->nic_id=(int)strtol(nic, &ptr, 10);
96 /* Check if there were any digits at all */
97 if(ptr == nic) nh_ptr->nic_id=NULL_NIC_ID;
98 }
99 /* check if there is any suffix */
100 if (*ptr == '\0') nh_ptr->source=NULL;
101 /* Copy suffix into source */
102 else {
103 if(!(nh_ptr->source=malloc(strlen(ptr)+1))) die;
104 strcpy(nh_ptr->source, ptr);
105 }
106 }
107 *nh_ptr_ptr=nh_ptr;
108 return(res);
109 }
110
111
112
113 /************************************************************
114 * int NH_check() *
115 * *
116 * Check a NIC handle in the repository *
117 * *
118 * *
119 * Returns: *
120 * 1 - success *
121 * 0 - error(nic_id exists or space is fully occupied) *
122 * -1 - error (f.e. more than one object with the same PK) *
123 * *
124 ************************************************************/
125 int NH_check(nic_handle_t *nh_ptr, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
126 {
127 range_t range;
128 long range_id;
129 long nic_id=nh_ptr->nic_id;
130
131
132 range.space=nh_ptr->space;
133 if(nh_ptr->source)range.source=nh_ptr->source; else range.source="";
134
135 if (nic_id == AUTO_NIC_ID) {
136 /* NIC handle is an AUTO one */
137 /* get first range (with min range_end) for a given space */
138 range_id = get_min_range(&range, sql_connection);
139 if(range_id<0) return(-1); /* in case of an error */
140
141 if ( range_id==0 ) {
142 /* Nothing found */
143 /* Allocate a hic-hdl in a new space with the first range {0-1} in it*/
144 nic_id=1;
145 } else {
146 if ( range.end == MAX_NIC_ID ) return(0); /* space is fully occupied */
147 /* attach to range and may be join with next */
148 nic_id = range.end+1;
149 }
150 }
151 /* if not AUTO */
152 else {
153 range_id = get_range(nic_id, &range, sql_connection);
154 if(range_id <0) return(-1); /* in case of an error */
155 if(range_id!=0) return(0); /* this nic_id already exists */
156 }
157 nh_ptr->nic_id=nic_id;
158 return(1);
159 }
160
161 /************************************************************
162 * long NH_free() *
163 * *
164 * Delete a NIC handle from the repository *
165 * *
166 * To finalize changes make commit/rollback *
167 * *
168 * Returns: *
169 * 1 - success *
170 * 0 - error (range is not founnd) *
171 * -1 - error (f.e. more than one object with the same PK) *
172 * *
173 ************************************************************/
174 int NH_free(nic_handle_t *nh_ptr, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
175 {
176 range_t range;
177 long range_id;
178 int old_start;
179 long nic_id=nh_ptr->nic_id;
180
181
182 range.space=nh_ptr->space;
183 if(nh_ptr->source)range.source=nh_ptr->source; else range.source="";
184
185 /* Search for the range containing the nic-handle */
186 range_id = get_range(nic_id, &range, sql_connection);
187 /* If range is not found or an error occcured - return */
188 if(range_id==0) { return(0); }
189 if(range_id<0) { return(-1); }
190
191 if(nic_id == range.start) {
192 /* update range start and may be detele range and space */
193 range.start+=1;
194 range_id=update_range(range_id, &range, sql_connection);
195 if(range_id<=0) { return(-1); }
196 }
197 else if(nic_id == range.end) {
198 /* update range end and may be detele range and space */
199 range.end-=1;
200 range_id=update_range(range_id, &range, sql_connection);
201 if(range_id<=0) { return(-1); }
202 }
203 else {
204 /* split the range into two */
205 /* shrink the old one */
206 old_start=range.start;
207 range.start=nic_id+1;
208 range_id=update_range(range_id, &range, sql_connection);
209 if(range_id<=0) { return(-1); }
210 /* create a new one */
211 range.start=old_start;
212 range.end=nic_id-1;
213 range_id=create_range(&range, sql_connection);
214 if(range_id<=0) { return(-1); }
215 }
216
217 return(1);
218 }
219
220
221 /************************************************************
222 * int NH_register() *
223 * *
224 * Get a NIC handle from the repository *
225 * *
226 * *
227 * Returns: *
228 * 1 - success *
229 * 0 - nic_id already exists or space is fully occupied *
230 * -1 - error (f.e. more than one object with the same PK) *
231 * *
232 ************************************************************/
233 int NH_register(nic_handle_t *nh_ptr, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
234 {
235 range_t range;
236 long range_id;
237 long nic_id=nh_ptr->nic_id;
238
239
240
241
242 /* Yiu should check for nh first for AUTO nic-handles */
243 if (nic_id == AUTO_NIC_ID) { return(0); };
244
245 range.space=nh_ptr->space;
246 if(nh_ptr->source)range.source=nh_ptr->source; else range.source="";
247
248 range_id = get_range(nic_id, &range, sql_connection);
249 if(range_id <0) { return(-1); } /* in case of an error */
250 if(range_id!=0) { return(0); } /* this nic_id already exists */
251
252 /* check if we can attach to existing next range */
253 range_id = get_range(nic_id+1, &range, sql_connection);
254 if(range_id <0) { return(-1); } /* in case of an error */
255
256 if( range_id>0 ) {
257 /* attach to range and may be join with previous */
258 range.start-=1;
259 range_id=update_range(range_id, &range, sql_connection);
260 if(range_id<=0) { return(-1); }
261 }
262 else {
263 /* check if we can attach to existing previous range */
264 if(nic_id>0) range_id = get_range(nic_id-1, &range, sql_connection);
265 else range_id=0; /* there is no previous range in this case (nic_id==0) */
266 if(range_id <0) { return(-1); } /* in case of an error */
267 if( range_id>0 ) {
268 /* attach to range and may be join with next */
269 range.end+=1;
270 range_id=update_range(range_id, &range, sql_connection);
271 if(range_id<=0) { return(-1); }
272 }
273 else {
274 /* If we cannot attach to any existing range - create new {nic_id-nic_id} */
275 range.end=range.start=nic_id;
276 range_id=create_range(&range, sql_connection);
277 if(range_id <=0) { return(-1); } /* in case of an error */
278 }
279 }
280 return(1);
281 }
282
283 /*
284 Free nic_handle_t structure
285 */
286 void free_nh(nic_handle_t *nh_ptr)
/* [<][>][^][v][top][bottom][index][help] */
287 {
288 if(nh_ptr){
289 if(nh_ptr->space)free(nh_ptr->space);
290 if(nh_ptr->source)free(nh_ptr->source);
291 free(nh_ptr);
292 }
293 }
294
295
296 /************************************************************
297 * long get_range() *
298 * *
299 * Searches for the range of the space containing *
300 * the specified nic_id *
301 * *
302 * To request to search for the firt (min) range, nic_id *
303 * should be set to -1. *
304 * *
305 * Returns: *
306 * >0 - range exists, returns range_id *
307 * 0 - range does not exist *
308 * -1 - DB error (f.e. more than one object with the same PK)*
309 * *
310 * **********************************************************/
311 static long get_range(long nic_id, range_t *prange, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
312 {
313 SQ_result_set_t *sql_result;
314 SQ_row_t *sql_row;
315 char *sql_str;
316 GString *query;
317 long range_id=0;
318 int sql_err;
319
320 if ((query = g_string_sized_new(STR_L)) == NULL){
321 fprintf(stderr, "E: cannot allocate gstring\n");
322 return(-1);
323 }
324
325 /* Define row numbers in the result of the query */
326 #define RANGE_ID 0
327 #define RANGE_START 1
328 #define RANGE_END 2
329
330 if (nic_id<0) {
331 /* requesting the first (min) range */
332 g_string_sprintf(query, "SELECT range_id, range_start, range_end "
333 "FROM nic_hdl "
334 "WHERE space='%s' "
335 "AND source='%s' "
336 "AND (range_start=0 "
337 "OR range_start=1) ",
338 prange->space, prange->source);
339 } else {
340
341 g_string_sprintf(query, "SELECT range_id, range_start, range_end "
342 "FROM nic_hdl "
343 "WHERE space='%s' "
344 "AND source='%s' "
345 "AND range_start<=%ld "
346 "AND range_end>=%ld ",
347 prange->space, prange->source, nic_id, nic_id);
348 }
349
350 /* execute query */
351 /* fprintf(stderr, "get_range[%s]\n", query->str); */
352 sql_err=SQ_execute_query(sql_connection, query->str, &sql_result);
353 g_string_free(query, TRUE);
354
355 if(sql_err) {
356 fprintf(stderr,"ERROR: %s\n", SQ_error(sql_connection));
357 return(-1);
358 }
359
360 if ((sql_row = SQ_row_next(sql_result)) != NULL) {
361 /* Object exists */
362 sql_str = SQ_get_column_string(sql_result, sql_row, RANGE_ID);
363 if (sql_str != NULL) {
364 range_id = atol(sql_str);
365 free(sql_str);
366 }
367 sql_str = SQ_get_column_string(sql_result, sql_row, RANGE_START);
368 if (sql_str != NULL) {
369 prange->start = atoi(sql_str);
370 free(sql_str);
371 }
372 sql_str = SQ_get_column_string(sql_result, sql_row, RANGE_END);
373 if (sql_str != NULL) {
374 prange->end = atoi(sql_str);
375 free(sql_str);
376 }
377
378 /* We must process all the rows of the result */
379 /* otherwise we'll have them as part of the next qry */
380 while ( (sql_row = SQ_row_next(sql_result)) != NULL) range_id=-1;
381 } else
382 range_id=0; // object does not exist
383
384 if(sql_result)SQ_free_result(sql_result);
385 return(range_id);
386 }
387
388
389
390
391 /************************************************************
392 * long update_range() *
393 * *
394 * Updates the range by changing the boundaries *
395 * Deletes the range if nothing left *
396 * Merges with neighbor ranges if there is no gap between *
397 * *
398 * We never update range. We create a new one with specified *
399 * limits and mark old one(s) for deletion, so that we can *
400 * make commit/rollback properly. This is possible as the *
401 * primary keys are (range_id, range_start, range_end) *
402 * *
403 * To finalize changes make commit/rollback *
404 * *
405 * Returns: *
406 * >0 - returns range_id on success *
407 * -1 - error (f.e. more than one object with the same PK) *
408 * *
409 ************************************************************/
410
411 static long update_range(long range_id, range_t *p_newrange, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
412 {
413 GString *query;
414 range_t range;
415 long prev_range_id, next_range_id;
416 int num;
417 int sql_err;
418
419 /* Allocate memory */
420 if ((query = g_string_sized_new(STR_L)) == NULL){
421 fprintf(stderr, "E: cannot allocate gstring\n");
422 return(-1);
423 }
424
425 /* Do range check */
426 if (( p_newrange->end > MAX_RANGE ) || ( p_newrange->start < MIN_RANGE )) return(-1);
427
428 /* Check if the range collapses */
429 if ( p_newrange->end < p_newrange->start ) {
430 /* then delete the range */
431 /* Do this by marking the range for deletion for further commit/rollback */
432 g_string_sprintf(query, "DELETE FROM nic_hdl "
433 "WHERE range_id=%ld ",
434 range_id);
435
436 /* fprintf(stderr, "update_range[%s]\n", query->str); */
437 sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
438 if(sql_err) {
439 /* An error occured */
440 g_string_free(query, TRUE);
441 return(-1);
442 }
443 num = mysql_affected_rows(sql_connection);
444 /* this should not happen */
445 if(num==0) die;
446
447 }
448 else {
449 /* update the range for the same space/source */
450 range.space=p_newrange->space;
451 range.source=p_newrange->source;
452 /* Check if we can join with previous range of the same space */
453 prev_range_id=get_range(p_newrange->start-1, &range, sql_connection);
454 /* Check if such range exists and it is not ours (this happens when we are shrinking */
455 if((prev_range_id>0) && (prev_range_id!=range_id)) {
456 /* acquire the previous range */
457 /* mark it for deletion for commit/rollback */
458 g_string_sprintf(query, "DELETE FROM nic_hdl "
459 "WHERE range_id=%ld ",
460 prev_range_id);
461
462 /* fprintf(stderr, "update_range[%s]\n", query->str); */
463 sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
464 if(sql_err) {
465 /* An error occured */
466 g_string_free(query, TRUE);
467 return(-1);
468 }
469 num = mysql_affected_rows(sql_connection);
470 /* this should not happen */
471 if(num==0) die;
472
473 /* expand the boundaries */
474 p_newrange->start=range.start;
475 }
476
477 /* Check if we can join with next range of the same space */
478 next_range_id=get_range(p_newrange->end+1, &range, sql_connection);
479 /* Check if such range exists and it is not ours (this happens when we are shrinking) */
480 if((next_range_id>0) && (next_range_id!=range_id)) {
481 /* acquire the next range */
482 /* mark it for deletion for commit/rollback */
483 g_string_sprintf(query, "DELETE FROM nic_hdl "
484 "WHERE range_id=%ld ",
485 next_range_id);
486
487 /* fprintf(stderr, "update_range[%s]\n", query->str); */
488 sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
489 if(sql_err) {
490 /* An error occured */
491 g_string_free(query, TRUE);
492 return(-1);
493 }
494 num = mysql_affected_rows(sql_connection);
495 /* this should not happen */
496 if(num==0) die;
497
498 /* expand the boundaries */
499 p_newrange->end=range.end;
500 }
501
502 /* Now make a larger range. Mark it for commit/rollback */
503 g_string_sprintf(query, "UPDATE nic_hdl "
504 "SET range_start=%ld, range_end=%ld "
505 "WHERE range_id=%ld",
506 p_newrange->start, p_newrange->end, range_id);
507
508 /* fprintf(stderr, "update_range[%s]\n", query->str); */
509 sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
510 if(sql_err) {
511 /* An error occured */
512 g_string_free(query, TRUE);
513 return(-1);
514 }
515 num = mysql_affected_rows(sql_connection);
516 /* this should not happen */
517 if(num==0) die;
518 } /* update the range */
519
520 g_string_free(query, TRUE);
521 return (range_id);
522 }
523
524 /************************************************************
525 * long create_range() *
526 * *
527 * Creates a new range in a given name space *
528 * *
529 * To finalize changes make commit/rollback *
530 * *
531 * Returns: *
532 * >0 - returns range_id on success *
533 * -1 - error (f.e. more than one object with the same PK) *
534 * *
535 ************************************************************/
536
537 static long create_range(range_t *p_range, SQ_connection_t *sql_connection)
/* [<][>][^][v][top][bottom][index][help] */
538 {
539 GString *query;
540 int sql_err, num;
541
542 /* Allocate memory */
543 if ((query = g_string_sized_new(STR_L)) == NULL){
544 fprintf(stderr, "E: cannot allocate gstring\n");
545 return(-1);
546 }
547
548
549 g_string_sprintf(query, "INSERT nic_hdl "
550 "SET space='%s', source='%s', range_start=%ld, range_end=%ld ",
551 p_range->space, p_range->source, p_range->start, p_range->end);
552
553 /* fprintf(stderr, "create_range[%s]\n", query->str); */
554 sql_err=SQ_execute_query(sql_connection, query->str, (SQ_result_set_t **)NULL);
555 g_string_free(query, TRUE);
556
557 if(sql_err) {
558 /* An error occured */
559 return(-1);
560 }
561 num = mysql_affected_rows(sql_connection);
562 /* this should not happen */
563 if(num==0) die;
564 return(mysql_insert_id(sql_connection));
565 }
566