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  |