1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * prof_get.c --- routines that expose the public interfaces for 4 * querying items from the profile. 5 * 6 */ 7 8 #include "prof_int.h" 9 #include <stdio.h> 10 #include <string.h> 11 #ifdef HAVE_STDLIB_H 12 #include <stdlib.h> 13 #endif 14 #include <errno.h> 15 #include <limits.h> 16 17 /* 18 * These functions --- init_list(), end_list(), and add_to_list() are 19 * internal functions used to build up a null-terminated char ** list 20 * of strings to be returned by functions like profile_get_values. 21 * 22 * The profile_string_list structure is used for internal booking 23 * purposes to build up the list, which is returned in *ret_list by 24 * the end_list() function. 25 * 26 * The publicly exported interface for freeing char** list is 27 * profile_free_list(). 28 */ 29 30 struct profile_string_list { 31 char **list; 32 unsigned int num; 33 unsigned int max; 34 }; 35 36 /* 37 * Initialize the string list abstraction. 38 */ 39 static errcode_t init_list(struct profile_string_list *list) 40 { 41 list->num = 0; 42 list->max = 10; 43 list->list = malloc(list->max * sizeof(char *)); 44 if (list->list == 0) 45 return ENOMEM; 46 list->list[0] = 0; 47 return 0; 48 } 49 50 /* 51 * Free any memory left over in the string abstraction, returning the 52 * built up list in *ret_list if it is non-null. 53 */ 54 static void end_list(struct profile_string_list *list, char ***ret_list) 55 { 56 char **cp; 57 58 if (list == 0) 59 return; 60 61 if (ret_list) { 62 *ret_list = list->list; 63 return; 64 } else { 65 for (cp = list->list; cp && *cp; cp++) 66 free(*cp); 67 free(list->list); 68 } 69 list->num = list->max = 0; 70 list->list = 0; 71 } 72 73 /* 74 * Add a string to the list. 75 */ 76 static errcode_t add_to_list(struct profile_string_list *list, const char *str) 77 { 78 char *newstr, **newlist; 79 unsigned int newmax; 80 81 if (list->num+1 >= list->max) { 82 newmax = list->max + 10; 83 newlist = realloc(list->list, newmax * sizeof(char *)); 84 if (newlist == 0) 85 return ENOMEM; 86 list->max = newmax; 87 list->list = newlist; 88 } 89 newstr = strdup(str); 90 if (newstr == 0) 91 return ENOMEM; 92 93 list->list[list->num++] = newstr; 94 list->list[list->num] = 0; 95 return 0; 96 } 97 98 /* 99 * Return TRUE if the string is already a member of the list. 100 */ 101 static int is_list_member(struct profile_string_list *list, const char *str) 102 { 103 char **cpp; 104 105 if (!list->list) 106 return 0; 107 108 for (cpp = list->list; *cpp; cpp++) { 109 if (!strcmp(*cpp, str)) 110 return 1; 111 } 112 return 0; 113 } 114 115 /* 116 * This function frees a null-terminated list as returned by 117 * profile_get_values. 118 */ 119 void KRB5_CALLCONV profile_free_list(char **list) 120 { 121 char **cp; 122 123 if (list == 0) 124 return; 125 126 for (cp = list; *cp; cp++) 127 free(*cp); 128 free(list); 129 } 130 131 /* Look up a relation in a vtable profile. */ 132 static errcode_t 133 get_values_vt(profile_t profile, const char *const *names, char ***ret_values) 134 { 135 errcode_t retval; 136 char **vtvalues, **val; 137 struct profile_string_list values; 138 139 retval = profile->vt->get_values(profile->cbdata, names, &vtvalues); 140 if (retval) 141 return retval; 142 143 /* Copy the result into memory we can free. */ 144 retval = init_list(&values); 145 if (retval == 0) { 146 for (val = vtvalues; *val; val++) 147 add_to_list(&values, *val); 148 end_list(&values, ret_values); 149 } 150 151 profile->vt->free_values(profile->cbdata, vtvalues); 152 return retval; 153 } 154 155 errcode_t KRB5_CALLCONV 156 profile_get_values(profile_t profile, const char *const *names, 157 char ***ret_values) 158 { 159 errcode_t retval; 160 void *state = NULL; 161 char *value; 162 struct profile_string_list values; 163 164 *ret_values = NULL; 165 if (!profile) 166 return PROF_NO_PROFILE; 167 if (profile->vt) 168 return get_values_vt(profile, names, ret_values); 169 170 if ((retval = profile_node_iterator_create(profile, names, 171 PROFILE_ITER_RELATIONS_ONLY, 172 &state))) 173 return retval; 174 175 retval = init_list(&values); 176 if (retval) 177 goto cleanup; 178 179 do { 180 if ((retval = profile_node_iterator(&state, 0, 0, &value))) 181 goto cleanup; 182 if (value) 183 add_to_list(&values, value); 184 } while (state); 185 186 if (values.num == 0) { 187 retval = PROF_NO_RELATION; 188 goto cleanup; 189 } 190 191 cleanup: 192 end_list(&values, retval ? NULL : ret_values); 193 profile_node_iterator_free(&state); 194 return retval; 195 } 196 197 /* Look up a relation in a vtable profile and return the first value in the 198 * result. */ 199 static errcode_t 200 get_value_vt(profile_t profile, const char *const *names, char **ret_value) 201 { 202 errcode_t retval; 203 char **vtvalues; 204 205 retval = profile->vt->get_values(profile->cbdata, names, &vtvalues); 206 if (retval) 207 return retval; 208 *ret_value = strdup(*vtvalues); 209 if (*ret_value == NULL) 210 retval = ENOMEM; 211 profile->vt->free_values(profile->cbdata, vtvalues); 212 return retval; 213 } 214 215 /* 216 * This function only gets the first value from the file; it is a 217 * helper function for profile_get_string, profile_get_integer, etc. 218 */ 219 errcode_t profile_get_value(profile_t profile, const char **names, 220 char **ret_value) 221 { 222 errcode_t retval; 223 void *state; 224 char *value; 225 226 *ret_value = NULL; 227 if (!profile) 228 return PROF_NO_PROFILE; 229 if (profile->vt) 230 return get_value_vt(profile, names, ret_value); 231 232 retval = profile_iterator_create(profile, names, 233 PROFILE_ITER_RELATIONS_ONLY, &state); 234 if (retval) 235 return retval; 236 237 retval = profile_iterator(&state, NULL, &value); 238 if (retval) 239 goto cleanup; 240 241 if (value) 242 *ret_value = value; 243 else 244 retval = PROF_NO_RELATION; 245 246 cleanup: 247 profile_iterator_free(&state); 248 return retval; 249 } 250 251 errcode_t KRB5_CALLCONV 252 profile_get_string(profile_t profile, const char *name, const char *subname, 253 const char *subsubname, const char *def_val, 254 char **ret_string) 255 { 256 char *value; 257 errcode_t retval; 258 const char *names[4]; 259 260 if (profile) { 261 names[0] = name; 262 names[1] = subname; 263 names[2] = subsubname; 264 names[3] = 0; 265 retval = profile_get_value(profile, names, &value); 266 if (retval == 0) { 267 *ret_string = value; 268 return 0; 269 } else if (retval != PROF_NO_SECTION && retval != PROF_NO_RELATION) 270 return retval; 271 } 272 273 if (def_val) { 274 *ret_string = strdup(def_val); 275 if (*ret_string == NULL) 276 return ENOMEM; 277 } else 278 *ret_string = NULL; 279 return 0; 280 } 281 282 static errcode_t 283 parse_int(const char *value, int *ret_int) 284 { 285 char *end_value; 286 long ret_long; 287 288 if (value[0] == 0) 289 /* Empty string is no good. */ 290 return PROF_BAD_INTEGER; 291 errno = 0; 292 ret_long = strtol(value, &end_value, 10); 293 294 /* Overflow or underflow. */ 295 if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) 296 return PROF_BAD_INTEGER; 297 /* Value outside "int" range. */ 298 if ((long) (int) ret_long != ret_long) 299 return PROF_BAD_INTEGER; 300 /* Garbage in string. */ 301 if (end_value != value + strlen (value)) 302 return PROF_BAD_INTEGER; 303 304 *ret_int = ret_long; 305 return 0; 306 } 307 308 errcode_t KRB5_CALLCONV 309 profile_get_integer(profile_t profile, const char *name, const char *subname, 310 const char *subsubname, int def_val, int *ret_int) 311 { 312 char *value; 313 errcode_t retval; 314 const char *names[4]; 315 316 *ret_int = def_val; 317 if (profile == 0) 318 return 0; 319 320 names[0] = name; 321 names[1] = subname; 322 names[2] = subsubname; 323 names[3] = 0; 324 retval = profile_get_value(profile, names, &value); 325 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 326 *ret_int = def_val; 327 return 0; 328 } else if (retval) 329 return retval; 330 331 retval = parse_int(value, ret_int); 332 free(value); 333 return retval; 334 } 335 336 static const char *const conf_yes[] = { 337 "y", "yes", "true", "t", "1", "on", 338 0, 339 }; 340 341 static const char *const conf_no[] = { 342 "n", "no", "false", "nil", "0", "off", 343 0, 344 }; 345 346 static errcode_t 347 profile_parse_boolean(const char *s, int *ret_boolean) 348 { 349 const char *const *p; 350 351 if (ret_boolean == NULL) 352 return PROF_EINVAL; 353 354 for(p=conf_yes; *p; p++) { 355 if (!strcasecmp(*p,s)) { 356 *ret_boolean = 1; 357 return 0; 358 } 359 } 360 361 for(p=conf_no; *p; p++) { 362 if (!strcasecmp(*p,s)) { 363 *ret_boolean = 0; 364 return 0; 365 } 366 } 367 368 return PROF_BAD_BOOLEAN; 369 } 370 371 errcode_t KRB5_CALLCONV 372 profile_get_boolean(profile_t profile, const char *name, const char *subname, 373 const char *subsubname, int def_val, int *ret_boolean) 374 { 375 char *value; 376 errcode_t retval; 377 const char *names[4]; 378 379 if (profile == 0) { 380 *ret_boolean = def_val; 381 return 0; 382 } 383 384 names[0] = name; 385 names[1] = subname; 386 names[2] = subsubname; 387 names[3] = 0; 388 retval = profile_get_value(profile, names, &value); 389 if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { 390 *ret_boolean = def_val; 391 return 0; 392 } else if (retval) 393 return retval; 394 395 retval = profile_parse_boolean(value, ret_boolean); 396 free(value); 397 return retval; 398 } 399 400 /* 401 * This function will return the list of the names of subections in the 402 * under the specified section name. 403 */ 404 errcode_t KRB5_CALLCONV 405 profile_get_subsection_names(profile_t profile, const char **names, 406 char ***ret_names) 407 { 408 errcode_t retval; 409 void *state; 410 char *name; 411 struct profile_string_list values; 412 413 if ((retval = profile_iterator_create(profile, names, 414 PROFILE_ITER_LIST_SECTION | 415 PROFILE_ITER_SECTIONS_ONLY, 416 &state))) 417 return retval; 418 419 if ((retval = init_list(&values))) 420 return retval; 421 422 do { 423 if ((retval = profile_iterator(&state, &name, NULL))) 424 goto cleanup; 425 if (name) 426 add_to_list(&values, name); 427 free(name); 428 } while (state); 429 430 end_list(&values, ret_names); 431 return 0; 432 433 cleanup: 434 end_list(&values, 0); 435 return retval; 436 } 437 438 /* 439 * This function will return the list of the names of relations in the 440 * under the specified section name. 441 */ 442 errcode_t KRB5_CALLCONV 443 profile_get_relation_names(profile_t profile, const char **names, 444 char ***ret_names) 445 { 446 errcode_t retval; 447 void *state; 448 char *name; 449 struct profile_string_list values; 450 451 if ((retval = profile_iterator_create(profile, names, 452 PROFILE_ITER_LIST_SECTION | 453 PROFILE_ITER_RELATIONS_ONLY, 454 &state))) 455 return retval; 456 457 if ((retval = init_list(&values))) 458 return retval; 459 460 do { 461 if ((retval = profile_iterator(&state, &name, NULL))) 462 goto cleanup; 463 if (name && !is_list_member(&values, name)) 464 add_to_list(&values, name); 465 free(name); 466 } while (state); 467 468 end_list(&values, ret_names); 469 return 0; 470 471 cleanup: 472 end_list(&values, 0); 473 return retval; 474 } 475 476 struct profile_iterator { 477 prf_magic_t magic; 478 profile_t profile; 479 void *idata; 480 }; 481 482 errcode_t KRB5_CALLCONV 483 profile_iterator_create(profile_t profile, const char *const *names, int flags, 484 void **ret_iter) 485 { 486 struct profile_iterator *iter; 487 errcode_t retval; 488 489 *ret_iter = NULL; 490 if (!profile) 491 return PROF_NO_PROFILE; 492 493 iter = malloc(sizeof(*iter)); 494 if (iter == NULL) 495 return ENOMEM; 496 iter->magic = PROF_MAGIC_ITERATOR; 497 iter->profile = profile; 498 499 /* Create the underlying iterator representation using the vtable or the 500 * built-in node iterator. */ 501 if (profile->vt) { 502 if (!profile->vt->iterator_create) 503 retval = PROF_UNSUPPORTED; 504 else 505 retval = profile->vt->iterator_create(profile->cbdata, names, 506 flags, &iter->idata); 507 } else { 508 retval = profile_node_iterator_create(profile, names, flags, 509 &iter->idata); 510 } 511 if (retval) { 512 free(iter); 513 return retval; 514 } 515 516 *ret_iter = iter; 517 return 0; 518 } 519 520 void KRB5_CALLCONV 521 profile_iterator_free(void **iter_p) 522 { 523 struct profile_iterator *iter; 524 profile_t profile; 525 526 if (!iter_p) 527 return; 528 iter = *iter_p; 529 if (!iter || iter->magic != PROF_MAGIC_ITERATOR) 530 return; 531 profile = iter->profile; 532 if (profile->vt) 533 profile->vt->iterator_free(profile->cbdata, iter->idata); 534 else 535 profile_node_iterator_free(&iter->idata); 536 free(iter); 537 *iter_p = NULL; 538 } 539 540 /* Make copies of name and value into *ret_name and *ret_value. Handle null 541 * values of any argument. */ 542 static errcode_t 543 set_results(const char *name, const char *value, char **ret_name, 544 char **ret_value) 545 { 546 char *name_copy = NULL, *value_copy = NULL; 547 548 if (ret_name && name) { 549 name_copy = strdup(name); 550 if (name_copy == NULL) 551 goto oom; 552 } 553 if (ret_value && value) { 554 value_copy = strdup(value); 555 if (value_copy == NULL) 556 goto oom; 557 } 558 if (ret_name) 559 *ret_name = name_copy; 560 if (ret_value) 561 *ret_value = value_copy; 562 return 0; 563 oom: 564 free(name_copy); 565 free(value_copy); 566 return ENOMEM; 567 } 568 569 errcode_t KRB5_CALLCONV 570 profile_iterator(void **iter_p, char **ret_name, char **ret_value) 571 { 572 char *name, *value; 573 errcode_t retval; 574 struct profile_iterator *iter = *iter_p; 575 profile_t profile; 576 577 if (ret_name) 578 *ret_name = NULL; 579 if (ret_value) 580 *ret_value = NULL; 581 if (iter == NULL || iter->magic != PROF_MAGIC_ITERATOR) 582 return PROF_MAGIC_ITERATOR; 583 profile = iter->profile; 584 585 if (profile->vt) { 586 retval = profile->vt->iterator(profile->cbdata, iter->idata, &name, 587 &value); 588 if (retval) 589 return retval; 590 if (name == NULL) { 591 profile->vt->iterator_free(profile->cbdata, iter->idata); 592 free(iter); 593 *iter_p = NULL; 594 } 595 retval = set_results(name, value, ret_name, ret_value); 596 if (name) 597 profile->vt->free_string(profile->cbdata, name); 598 if (value) 599 profile->vt->free_string(profile->cbdata, value); 600 return retval; 601 } 602 603 retval = profile_node_iterator(&iter->idata, 0, &name, &value); 604 if (iter->idata == NULL) { 605 free(iter); 606 *iter_p = NULL; 607 } 608 if (retval) 609 return retval; 610 return set_results(name, value, ret_name, ret_value); 611 } 612 613 void KRB5_CALLCONV 614 profile_release_string(char *str) 615 { 616 free(str); 617 } 618