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
get_bsize(cfp_t * cfp,char * name)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
get_block_size(int size)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 *
get_block_buf(int size)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
free_block_buf(char * buf)141 free_block_buf(char *buf)
142 {
143 if (buf)
144 free(buf);
145 }
146
147 void
localcf_close(cfp_t * cfp)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 *
localcf_open(cfp_t * cfp,char * name)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
localcf_seekblk(cfp_t * cfp,int off,int mode)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
localcf_readblk(cfp_t * cfp,void * buf,int size)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
localcf_writeblk(cfp_t * cfp,void * buf,int size)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
localcf_seek(cfp_t * cfp,int off,int mode)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
localcf_read(cfp_t * cfp,void * buf,int size)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
localcf_write(cfp_t * cfp,void * buf,int size)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
addcfline(cfp_t * cfp,char * line,int table_index)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
remcfline(cfp_t * cfp,int table_offset,int setnum)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 *
readcfline(cfp_t * cfp,char * buf,int table_offset,int num)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
replacecfline(cfp_t * cfp,char * line,int table_offset,int num)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 *
cfg_raw_io_provider(void)644 cfg_raw_io_provider(void)
645 {
646 return (&_cfg_raw_io_def);
647 }
648
649 cfg_io_t *
cfg_block_io_provider(void)650 cfg_block_io_provider(void)
651 {
652 return (&_cfg_block_io_def);
653 }
654