Source File modules/nh/nh.c

    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  |