1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 28 #include <sys/types.h> 29 #include <sys/vtoc.h> 30 #include <sys/wait.h> 31 #include <stdio.h> 32 #include <sys/mnttab.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <fcntl.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <sys/mman.h> 41 42 #include <locale.h> 43 #include <langinfo.h> 44 #include <libintl.h> 45 #include <stdarg.h> 46 #include <netdb.h> 47 #include <ctype.h> 48 #include <sys/stat.h> 49 #include <sys/utsname.h> 50 51 #include "cfg_impl.h" 52 #include "cfg.h" 53 #include "cfg_local.h" 54 55 #if 0 56 #define DEBUG_CFGLIST 57 #define DEBUG_CFGLISTRM 58 #endif 59 60 extern int cfg_severity; 61 extern char *cfg_perror_str; 62 63 long 64 get_bsize(cfp_t *cfp, char *name) 65 { 66 char char_name[PATH_MAX]; 67 char *rest; 68 struct vtoc vtoc; 69 int slice; 70 int fd; 71 72 if (strlen(name) >= PATH_MAX - 1) 73 return (0); 74 75 rest = strstr(name, "/dsk/"); 76 if (rest == NULL) { 77 if ((rest = strstr(name, "/rdsk/")) == NULL) 78 return (0); 79 strcpy(char_name, name); 80 goto do_open; 81 82 } 83 strcpy(char_name, name); 84 char_name[strlen(name) - strlen(rest)] = 0; 85 strcat(char_name, "/rdsk/"); 86 strcat(char_name, rest + 5); 87 88 do_open: 89 fd = open(char_name, O_RDONLY); 90 if (fd < 0) 91 return (0); 92 93 slice = read_vtoc(fd, &vtoc); 94 if (slice < 0) { 95 (void) close(fd); 96 return (0); 97 } 98 99 (void) close(fd); 100 if (vtoc.v_part[slice].p_start < CFG_VTOC_SIZE) 101 cfp->cf_flag |= CFG_NOWRVTOC; 102 103 return (vtoc.v_part[slice].p_size); 104 } 105 106 /* 107 * round up to the next block size 108 */ 109 int 110 get_block_size(int size) 111 { 112 int ret; 113 114 if (size % CFG_BLOCK_SIZE != 0) 115 ret = size + CFG_BLOCK_SIZE - (size % CFG_BLOCK_SIZE); 116 else 117 ret = size; 118 return (ret); 119 } 120 121 /* 122 * get a chunk of mem rounded up to next block size 123 */ 124 char * 125 get_block_buf(int size) 126 { 127 int blk_size; 128 char *blk_buf; 129 130 blk_size = get_block_size(size); 131 132 if ((blk_buf = (char *)calloc(blk_size, sizeof (char))) == NULL) { 133 cfg_severity = CFG_EFATAL; 134 cfg_perror_str = dgettext("cfg", strerror(errno)); 135 return (NULL); 136 } 137 return (blk_buf); 138 } 139 140 void 141 free_block_buf(char *buf) 142 { 143 if (buf) 144 free(buf); 145 } 146 147 void 148 localcf_close(cfp_t *cfp) 149 { 150 fsync(cfp->cf_fd); 151 cfp_unlock(cfp); 152 close(cfp->cf_fd); 153 } 154 155 156 /* 157 * cfg_open 158 * Open the current configuration file 159 * Sets file descriptor in cfp->cf_fd for use by other routines 160 */ 161 cfp_t * 162 localcf_open(cfp_t *cfp, char *name) 163 { 164 struct stat sb; 165 int rc; 166 167 168 if (name == NULL) { 169 cfg_perror_str = dgettext("cfg", 170 "cfg_open: unable to open configuration location"); 171 cfg_severity = CFG_EFATAL; 172 return (NULL); 173 } 174 175 cfp->cf_fd = open(name, O_RDWR|O_CREAT|O_DSYNC|O_RSYNC, 0640); 176 if (cfp->cf_fd == -1) { 177 if ((cfp->cf_fd = open(name, O_RDONLY, 0640)) == -1) { 178 cfg_perror_str = dgettext("cfg", 179 "cfg_open: unable to open configuration location"); 180 cfg_severity = CFG_EFATAL; 181 return (NULL); 182 } 183 cfp->cf_flag |= CFG_RDONLY; 184 } 185 186 if (fstat(cfp->cf_fd, &sb) == -1) { 187 close(cfp->cf_fd); 188 cfg_perror_str = dgettext("cfg", 189 "cfg_open: unable to stat configuration location"); 190 cfg_severity = CFG_EFATAL; 191 return (NULL); 192 } 193 194 195 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 196 cfp->cf_size = get_bsize(cfp, name); 197 198 /* skip the vtoc if necessary */ 199 if (cfp->cf_flag & CFG_NOWRVTOC) { 200 do { 201 rc = lseek(cfp->cf_fd, CFG_VTOC_SKIP, SEEK_SET); 202 } while (rc == -1 && errno == EINTR); 203 204 if (rc == -1) { 205 cfg_perror_str = dgettext("cfg", 206 strerror(errno)); 207 cfg_severity = CFG_EFATAL; 208 close(cfp->cf_fd); 209 return (NULL); 210 } 211 } 212 213 } else if (S_ISREG(sb.st_mode)) { 214 cfp->cf_flag |= CFG_FILE; 215 cfp->cf_size = FBA_NUM(FBA_SIZE(1) - 1 + sb.st_size); 216 } else { 217 cfg_perror_str = dgettext("cfg", "cfg_open: unknown file type"); 218 cfg_severity = CFG_EFATAL; 219 close(cfp->cf_fd); 220 cfp->cf_fd = NULL; 221 return (NULL); 222 } 223 return (cfp); 224 } 225 226 int 227 localcf_seekblk(cfp_t *cfp, int off, int mode) 228 { 229 int rc; 230 231 do { 232 rc = lseek(cfp->cf_fd, off, mode); 233 } while (rc == -1 && errno == EINTR); 234 235 return (rc); 236 } 237 238 int 239 localcf_readblk(cfp_t *cfp, void *buf, int size) 240 { 241 int rc; 242 243 do { 244 rc = read(cfp->cf_fd, buf, size); 245 } while (rc == -1 && errno == EINTR); 246 247 return (rc); 248 } 249 250 int 251 localcf_writeblk(cfp_t *cfp, void *buf, int size) 252 { 253 int rc; 254 255 do { 256 rc = write(cfp->cf_fd, buf, size); 257 } while (rc == -1 && errno == EINTR); 258 259 return (rc); 260 } 261 262 int 263 localcf_seek(cfp_t *cfp, int off, int mode) 264 { 265 int rc; 266 int offset; 267 268 offset = get_block_size(off); 269 270 if ((mode == SEEK_SET) && (cfp->cf_flag & CFG_NOWRVTOC)) { 271 offset += CFG_VTOC_SKIP; 272 } 273 274 do { 275 rc = lseek(cfp->cf_fd, offset, mode); 276 } while (rc == -1 && errno == EINTR); 277 278 return (rc); 279 } 280 281 int 282 localcf_read(cfp_t *cfp, void *buf, int size) 283 { 284 int rc; 285 int blk_size; 286 char *blk_buf; 287 288 blk_size = get_block_size(size); 289 if ((blk_buf = get_block_buf(size)) == NULL) 290 return (-1); 291 292 do { 293 rc = read(cfp->cf_fd, blk_buf, blk_size); 294 } while (rc == -1 && errno == EINTR); 295 296 bcopy(blk_buf, buf, size); 297 free_block_buf(blk_buf); 298 299 return (rc); 300 } 301 302 int 303 localcf_write(cfp_t *cfp, void *buf, int size) 304 { 305 int rc; 306 int blk_size; 307 char *blk_buf; 308 309 blk_size = get_block_size(size); 310 if ((blk_buf = get_block_buf(size)) == NULL) 311 return (-1); 312 313 bcopy(buf, blk_buf, size); 314 315 do { 316 rc = write(cfp->cf_fd, blk_buf, blk_size); 317 } while (rc == -1 && errno == EINTR); 318 319 free_block_buf(blk_buf); 320 321 return (rc); 322 } 323 /* 324 * Routines which operate on internal version of configuration 325 */ 326 327 /* 328 * Add entry to end of configuration section 329 */ 330 331 int 332 addcfline(cfp_t *cfp, char *line, int table_index) 333 { 334 int len = strlen(line)+1; 335 int newsize = DEFAULT_ENTRY_SIZE / 2; 336 cfgheader_t *hd; 337 cfglist_t *cfl; 338 char *q; 339 340 #ifdef DEBUG_CFGLIST 341 fprintf(stderr, "addcfline: pre l_size %d h_cfgsizes[%d]" 342 " %d l_free %u adding len %d\n", 343 cfp->cf_head->h_cfgs[table_index].l_size, table_index, 344 cfp->cf_head->h_cfgsizes[table_index], 345 cfp->cf_head->h_cfgs[table_index].l_free, len); 346 #endif 347 348 hd = cfp->cf_head; 349 cfl = &cfp->cf_head->h_cfgs[table_index]; 350 if (cfl->l_free < len) { 351 352 #ifdef DEBUG_CFGLIST 353 fprintf(stderr, "resizing l_entry from %d to %d\n", 354 cfl->l_size + cfl->l_free, cfl->l_size + 355 cfl->l_free + newsize); 356 #endif 357 cfl->l_entry = (char *)realloc(cfl->l_entry, (cfl->l_size + 358 cfl->l_free + newsize) * sizeof (char)); 359 if (cfl->l_entry == NULL) { 360 errno = ENOMEM; 361 return (-1); 362 } 363 cfl->l_free += newsize; 364 365 } 366 cfl->l_free -= len; 367 368 /* out of list slots, get some more */ 369 if (cfl->l_nentry % DEFAULT_NENTRIES == 0) { 370 /* 371 * first, figure out how much bigger, than realloc 372 */ 373 374 #ifdef DEBUG_CFGLIST 375 fprintf(stderr, 376 "list %d getting more nentries, I have %d\n", 377 table_index, cfl->l_nentry); 378 #endif 379 cfl->l_esiz = (int *) 380 realloc(cfl->l_esiz, (cfl->l_nentry + DEFAULT_NENTRIES) * 381 sizeof (int)); 382 if (cfl->l_esiz == NULL) { 383 errno = ENOMEM; 384 return (-1); 385 } 386 } 387 388 389 cfl->l_esiz[cfl->l_nentry] = len; 390 cfl->l_nentry++; 391 392 /* add line to end of list */ 393 q = cfl->l_entry + cfl->l_size; 394 395 strcpy(q, line); 396 q += len; 397 398 /* set sizes */ 399 hd->h_cfgs[table_index].l_size += len; 400 hd->h_cfgsizes[table_index] = cfl->l_size; 401 cfp->cf_head->h_csize += len; 402 403 #ifdef DEBUG_CFGLIST 404 fprintf(stderr, "addcfline: post l_size %d h_cfgsizes[%d]" 405 " %d l_free %u\n h_csize %d\n", 406 cfp->cf_head->h_cfgs[table_index].l_size, 407 table_index, cfp->cf_head->h_cfgsizes[table_index], 408 cfp->cf_head->h_cfgs[table_index].l_free, cfp->cf_head->h_csize); 409 #endif 410 411 return (1); 412 } 413 414 /* 415 * remove entry from configuration section 416 */ 417 int 418 remcfline(cfp_t *cfp, int table_offset, int setnum) 419 { 420 cfgheader_t *ch; 421 char *p, *q; 422 int len; 423 int copylen; 424 int i; 425 cfglist_t *cfl; 426 ch = cfp->cf_head; 427 428 cfl = &cfp->cf_head->h_cfgs[table_offset]; 429 430 q = cfl->l_entry; 431 432 if (cfl->l_size == 0) { 433 /* list is empty */ 434 return (-1); 435 } 436 437 if (!q) { /* somethings wrong here */ 438 return (-1); 439 } 440 441 442 for (i = 1; i < setnum; i++) { 443 q += cfl->l_esiz[i - 1]; 444 if (i >= cfl->l_nentry) { /* end of list */ 445 return (-1); 446 } 447 } 448 449 if (q >= cfl->l_entry + cfl->l_size) 450 return (-1); 451 452 len = cfl->l_esiz[i - 1]; 453 454 455 #ifdef DEBUG_CFGLISTRM 456 fprintf(stderr, "remcfline: pre: l_size %d h_cfgsizes[%d] %d free %d" 457 " removing len %d\n", 458 ch->h_cfgs[table_offset].l_size, table_offset, 459 ch->h_cfgsizes[table_offset], 460 ch->h_cfgs[table_offset].l_free, len); 461 #endif 462 463 p = q + len; /* next string */ 464 465 if (!(p >= cfl->l_entry + cfl->l_size)) { 466 /* if we didn't delete the last string in list */ 467 /* LINTED possible overflow */ 468 copylen = cfl->l_entry + cfl->l_size - p; 469 bcopy(p, q, copylen); 470 copylen = (cfl->l_nentry - i) * sizeof (int); 471 bcopy(&cfl->l_esiz[i], &cfl->l_esiz[i - 1], copylen); 472 } 473 474 /* decrement the number of sets in this list */ 475 cfl->l_nentry--; 476 /* not really necessary, but.. */ 477 cfl->l_esiz[cfl->l_nentry] = 0; 478 479 cfl->l_size -= len; 480 cfl->l_free += len; 481 482 p = cfl->l_entry + cfl->l_size; 483 bzero(p, cfl->l_free); 484 485 ch->h_cfgsizes[table_offset] = cfl->l_size; 486 ch->h_csize -= len; 487 488 489 #ifdef DEBUG_CFGLIST 490 fprintf(stderr, 491 "remcfline: post: l_size %d h_cfgsizes[%d] %d free %d\n ", 492 ch->h_cfgs[table_offset].l_size, table_offset, 493 ch->h_cfgsizes[table_offset], ch->h_cfgs[table_offset].l_free); 494 #endif 495 496 return (0); 497 498 } 499 /* 500 * Read entry from configuration section 501 */ 502 char * 503 readcfline(cfp_t *cfp, char *buf, int table_offset, int num) 504 { 505 506 char *q; 507 int i; 508 cfgheader_t *ch; 509 cfglist_t *cfl; 510 511 /* this means they couldn't even find it in the parser tree */ 512 if (table_offset < 0) 513 return (NULL); 514 515 ch = cfp->cf_head; 516 cfl = &ch->h_cfgs[table_offset]; 517 518 q = cfl->l_entry; 519 520 for (i = 1; i < num; i++) { 521 q += cfl->l_esiz[i - 1]; 522 if (i >= cfl->l_nentry) /* end of list */ 523 return (NULL); 524 } 525 526 if (q >= cfl->l_entry + cfl->l_size) 527 return (NULL); 528 strcpy(buf, q); 529 return (q); 530 } 531 532 533 /* 534 * overwrite from current position with new value 535 */ 536 int 537 replacecfline(cfp_t *cfp, char *line, int table_offset, int num) 538 { 539 /* 540 * take a table offset and a num to replace 541 * index in, bump the list up, leaving a hole big 542 * enough for the new string, or bcopying the rest of the list 543 * down only leaving a hole big enough. 544 * make sure not to overflow the 545 * allocated list size. 546 */ 547 cfgheader_t *ch; 548 cfglist_t *cfl; 549 char *p, *q; 550 int len = strlen(line) + 1; 551 int diff = 0; 552 int i; 553 int newsize = DEFAULT_ENTRY_SIZE / 2; 554 555 556 ch = cfp->cf_head; 557 cfl = &ch->h_cfgs[table_offset]; 558 559 q = cfl->l_entry; 560 for (i = 1; i < num; i++) { 561 q += cfl->l_esiz[i - 1]; 562 if (i >= cfl->l_nentry) /* end of list */ 563 return (-1); 564 } 565 diff = len - cfl->l_esiz[i - 1]; 566 /* check for > 0, comparing uint to int */ 567 if ((diff > 0) && (diff > cfl->l_free)) { 568 /* 569 * we are going to overflow, get more mem, but only 570 * 1/2 as much as initial calloc, we don't need to be greedy 571 */ 572 #ifdef DEBUG_CFGLIST 573 fprintf(stderr, 574 "resizing at replacecfline from %d to %d \n", 575 cfl->l_size + cfl->l_free, cfl->l_size + 576 cfl->l_free + newsize); 577 #endif 578 cfl->l_entry = (char *)realloc(cfl->l_entry, 579 (cfl->l_size + cfl->l_free + newsize) * sizeof (char)); 580 if (cfl->l_entry == NULL) { 581 errno = ENOMEM; 582 return (-1); 583 } 584 cfl->l_free += (DEFAULT_ENTRY_SIZE / 2); 585 586 /* re-find q, we could have a whole new chunk of memory here */ 587 q = cfl->l_entry; 588 for (i = 1; i < num; i++) { 589 q += cfl->l_esiz[i - 1]; 590 if (i >= cfl->l_nentry) /* end of list */ 591 return (-1); 592 } 593 } 594 595 p = q + cfl->l_esiz[i - 1]; /* next string */ 596 cfl->l_esiz[i - 1] += diff; /* the new entry size */ 597 if (diff != 0) { /* move stuff over/back for correct fit */ 598 /* LINTED possible overflow */ 599 bcopy(p, p + diff, (cfl->l_entry + cfl->l_size - p)); 600 cfl->l_free -= diff; /* 0 - (-1) = 1 */ 601 cfl->l_size += diff; 602 603 /* total of all h_cfgs[n].l_entry */ 604 cfp->cf_head->h_csize += diff; 605 cfp->cf_head->h_cfgsizes[table_offset] = cfl->l_size; /* disk */ 606 bzero((cfl->l_entry + cfl->l_size), cfl->l_free); 607 } 608 609 strcpy(q, line); 610 return (1); 611 612 } 613 614 static cfg_io_t _cfg_raw_io_def = { 615 NULL, 616 "Local", 617 localcf_open, 618 localcf_close, 619 localcf_seek, 620 localcf_read, 621 localcf_write, 622 readcfline, 623 addcfline, 624 remcfline, 625 replacecfline, 626 627 }; 628 629 static cfg_io_t _cfg_block_io_def = { 630 NULL, 631 "Local", 632 localcf_open, 633 localcf_close, 634 localcf_seekblk, 635 localcf_readblk, 636 localcf_writeblk, 637 readcfline, 638 addcfline, 639 remcfline, 640 replacecfline, 641 }; 642 643 cfg_io_t * 644 cfg_raw_io_provider(void) 645 { 646 return (&_cfg_raw_io_def); 647 } 648 649 cfg_io_t * 650 cfg_block_io_provider(void) 651 { 652 return (&_cfg_block_io_def); 653 }