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 |