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 }