xref: /titanic_44/usr/src/lib/libdscfg/common/cfg_local.c (revision b695575577bae0337af339d76949713bfe1c9013)
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 }
654