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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 |