## modules/nh/nh.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. get_min_range
2. NH_convert
3. NH_parse
4. NH_check
5. NH_free
6. NH_register
7. free_nh
8. get_range
9. update_range
10. 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

/* [<][>][^][v][top][bottom][index][help] */