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 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * Just in case we're not in a build environment, make sure that
30 * TEXT_DOMAIN gets set to something.
31 */
32#if !defined(TEXT_DOMAIN)
33#define	TEXT_DOMAIN "SYS_TEST"
34#endif
35
36/*
37 * patch /kernel/drv/md.conf file
38 */
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <meta.h>
42#include <sys/lvm/md_mddb.h>
43
44/*
45 * magic strings in system
46 */
47#define	BEGROOTSTR	"* Begin MDD root info (do not edit)\n"
48#define	ENDROOTSTR	"* End MDD root info (do not edit)\n"
49#define	BEGMDDBSTR	"# Begin MDD database info (do not edit)\n"
50#define	ENDMDDBSTR	"# End MDD database info (do not edit)\n"
51
52/*
53 * copy system file, yank root and database lines
54 */
55int
56meta_systemfile_copy(
57	char		*sname,		/* system file name */
58	int		doroot,		/* remove mdd root stuff */
59	int		domddb,		/* remove mdd database stuff */
60	int		doit,		/* really copy file */
61	int		verbose,	/* show what we're doing */
62	char		**tname,	/* returned temp file name */
63	FILE		**tfp,		/* returned open FILE */
64	md_error_t	*ep		/* returned error */
65)
66{
67	FILE		*fp;
68	struct stat	sbuf;
69	char		buf[MDDB_BOOTLIST_MAX_LEN];
70	int		delroot = 0;
71	int		delmddb = 0;
72
73	/* check names */
74	assert(sname != NULL);
75	assert(tname != NULL);
76	assert(tfp != NULL);
77
78	/* get temp name */
79	*tfp = NULL;
80	*tname = Malloc(strlen(sname) + strlen(".tmp") + 1);
81	(void) strcpy(*tname, sname);
82	(void) strcat(*tname, ".tmp");
83
84	/* copy system file, yank stuff */
85	if (((fp = fopen(sname, "r")) == NULL) ||
86	    (fstat(fileno(fp), &sbuf) != 0)) {
87		if (errno != ENOENT) {
88			(void) mdsyserror(ep, errno, sname);
89			goto out;
90		}
91	}
92	if (doit) {
93		if ((*tfp = fopen(*tname, "w")) == NULL) {
94			/*
95			 * If we are on the miniroot we need to create
96			 * files in /var/tmp. Opening a writable file
97			 * in the miniroot result is EROFS error.
98			 */
99			if (errno != EROFS) {
100				(void) mdsyserror(ep, errno, *tname);
101				goto out;
102			}
103			Free(*tname);
104			*tname = tempnam("/var/tmp", "svm_");
105			if (*tname == NULL) {
106				(void) mdsyserror(ep, errno, NULL);
107				goto out;
108			}
109			if ((*tfp = fopen(*tname, "w")) == NULL) {
110				(void) mdsyserror(ep, errno, *tname);
111				goto out;
112			}
113		}
114		if (fp != NULL) {
115			if ((fchmod(fileno(*tfp), (sbuf.st_mode & 0777))
116			    != 0) ||
117			    (fchown(fileno(*tfp), sbuf.st_uid, sbuf.st_gid)
118			    != 0)) {
119				(void) mdsyserror(ep, errno, *tname);
120				goto out;
121			}
122		}
123	}
124	if (verbose) {
125		(void) printf(dgettext(TEXT_DOMAIN,
126		    "Delete the following lines from %s:\n\n"), sname);
127	}
128	while ((fp != NULL) && (fgets(buf, sizeof (buf), fp) != NULL)) {
129		if ((doroot) && (strcmp(buf, BEGROOTSTR) == 0)) {
130			delroot = 1;
131			if (verbose)
132				(void) printf("%s", buf);
133			continue;
134		}
135		if (delroot) {
136			if (strcmp(buf, ENDROOTSTR) == 0)
137				delroot = 0;
138			if (verbose)
139				(void) printf("%s", buf);
140			continue;
141		}
142		if ((domddb) && (strcmp(buf, BEGMDDBSTR) == 0)) {
143			delmddb = 1;
144			if (verbose)
145				(void) printf("%s", buf);
146			continue;
147		}
148		if (delmddb) {
149			if (strcmp(buf, ENDMDDBSTR) == 0)
150				delmddb = 0;
151			if (verbose)
152				(void) printf("%s", buf);
153			continue;
154		}
155		if (doit) {
156			if (fputs(buf, *tfp) == EOF) {
157				(void) mdsyserror(ep, errno, *tname);
158				goto out;
159			}
160		}
161	}
162	if (fp != NULL) {
163		if ((! feof(fp)) ||
164		    (fclose(fp) != 0)) {
165			(void) mdsyserror(ep, errno, sname);
166			goto out;
167		}
168		fp = NULL;
169	}
170	if (verbose)
171		(void) printf("\n");
172
173	/* make sure we didn't stop mid-delete */
174	if ((delroot) || (delmddb)) {
175		(void) mderror(ep, MDE_SYSTEM_FILE, sname);
176		goto out;
177	}
178
179	/* flush stuff */
180	if (doit) {
181		if ((fflush(*tfp) != 0) ||
182		    (fsync(fileno(*tfp)) != 0)) {
183			(void) mdsyserror(ep, errno, *tname);
184			goto out;
185		}
186	}
187
188	/* return success */
189	return (0);
190
191	/* cleanup, return error */
192out:
193	if (fp != NULL)
194		(void) fclose(fp);
195	if (*tname != NULL) {
196		(void) unlink(*tname);
197		Free(*tname);
198	}
199	if (*tfp != NULL)
200		(void) fclose(*tfp);
201	return (-1);
202}
203
204/*
205 * append root on MD lines to system
206 */
207int
208meta_systemfile_append_mdroot(
209	mdname_t	*rootnp,	/* root device name */
210	char		*sname,		/* system file name */
211	char		*tname,		/* temp file name */
212	FILE		*tfp,		/* temp FILE */
213	int		ismeta,		/* is a metadevice */
214	int		doit,		/* really patch file */
215	int		verbose,	/* show what we're doing */
216	md_error_t	*ep
217)
218{
219	char		*longblkname;
220
221	/* check names */
222	assert(sname != NULL);
223	assert(tname != NULL);
224	assert(!doit || tfp != NULL);
225
226	/* get root /devices name */
227	if ((longblkname = metagetdevicesname(rootnp, ep)) == NULL)
228		return (-1);
229
230	/* add header */
231	if (verbose) {
232		(void) printf(dgettext(TEXT_DOMAIN,
233		    "Add the following lines to %s:\n\n"), sname);
234		(void) printf("%s", BEGROOTSTR);
235	}
236	if (doit) {
237		if (fprintf(tfp, "%s", BEGROOTSTR) == EOF) {
238			return (mdsyserror(ep, errno, tname));
239		}
240	}
241
242	/* add rootdev */
243	if (ismeta) {
244		if (verbose)
245			(void) printf("rootdev:%s\n", longblkname);
246		if (doit) {
247			if (fprintf(tfp, "rootdev:%s\n", longblkname) == EOF) {
248				return (mdsyserror(ep, errno, tname));
249			}
250		}
251	}
252
253	/* add trailer */
254	if (verbose) {
255		(void) printf("%s\n", ENDROOTSTR);
256	}
257	if (doit) {
258		if (fprintf(tfp, "%s", ENDROOTSTR) == EOF) {
259			return (mdsyserror(ep, errno, tname));
260		}
261	}
262
263	/* flush stuff */
264	if (doit) {
265		if ((fflush(tfp) != 0) ||
266		    (fsync(fileno(tfp)) != 0)) {
267			return (mdsyserror(ep, errno, tname));
268		}
269	}
270
271	/* return success */
272	return (0);
273}
274
275/*
276 * parse mddb.cf line
277 *
278 * Caller of this routine needs to free the device id string that
279 * is passed back during a successful return.
280 */
281static int
282confline(
283	char		*line,		/* line in file */
284	char		**driver,	/* returned driver name */
285	minor_t		*mnump,		/* returned minor number */
286	daddr_t		*block,		/* returned block offset */
287	char		**devid_char_pp	/* returned device id string */
288)
289{
290	char		*p = line;
291	int		chksum = 0;
292	int		i;
293	uint_t		devid_size;
294
295	if (*p == '#') {
296		return (-1);
297	}
298	*driver = p;
299	while ((*p != ' ') && (*p != '\t'))
300		chksum += *p++;
301	if (*driver == p) {
302		return (-1);
303	}
304	*p++ = '\0';
305	*mnump = strtoul(p, &p, 10);
306	chksum += *mnump;
307	*block = strtol(p, &p, 10);
308	chksum += *block;
309
310	/* parse out devid */
311	while ((*p == ' ') || (*p == '\t')) {
312		p++;
313	}
314	i = strcspn(p, " \t");
315	*devid_char_pp = Malloc(i+1);
316	(void) strncpy(*devid_char_pp, p, i);
317	(*devid_char_pp)[i] = '\0';
318	devid_size = i;
319	p += devid_size;
320	for (i = 0; i < devid_size; i++) {
321		chksum += (*devid_char_pp)[i];
322	}
323
324	chksum += strtol(p, &p, 10);
325	if (chksum != 42) {
326		Free (*devid_char_pp);
327		devid_char_pp = NULL;
328		return (-1);
329	}
330	return (0);
331}
332
333/*
334 * append MDDB lines to system
335 */
336int
337meta_systemfile_append_mddb(
338	char		*cname,		/* mddb.cf file name */
339	char		*sname,		/* system file name */
340	char		*tname,		/* temp file name */
341	FILE		*tfp,		/* temp FILE */
342	int		doit,		/* really patch file */
343	int		verbose,	/* show what we're doing */
344	int		check,		/* if set check that mddb.cf is not */
345					/* empty before updating md.conf    */
346	md_error_t	*ep		/* returned error */
347)
348{
349	FILE		*cfp = NULL;
350	char		buf[1024];
351	char		*p;
352	int		i;
353	char		*driver;
354	minor_t		mnum;
355	daddr_t		block;
356	char		line[MDDB_BOOTLIST_MAX_LEN];
357	char		entry[MDDB_BOOTLIST_MAX_LEN];
358	char		*devid_char_p = NULL;
359	struct stat	statbuf;
360
361	/* check names */
362	assert(cname != NULL);
363	assert(sname != NULL);
364	assert(tname != NULL);
365	assert(!doit || tfp != NULL);
366
367	/* open database conf file */
368	if ((cfp = fopen(cname, "r")) == NULL) {
369		(void) mdsyserror(ep, errno, cname);
370		goto out;
371	}
372	/* Check that it is an ordinary file */
373	if (stat(cname, &statbuf) != 0) {
374		(void) mdsyserror(ep, errno, cname);
375		goto out;
376	}
377	if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
378		(void) mderror(ep, MDE_MDDB_FILE, cname);
379		goto out;
380	}
381
382	/* add header */
383	if (verbose) {
384		(void) printf(dgettext(TEXT_DOMAIN,
385		    "Add the following lines to %s:\n\n"), sname);
386		(void) printf("%s", BEGMDDBSTR);
387	}
388	if (doit) {
389		if (fprintf(tfp, "%s", BEGMDDBSTR) == EOF) {
390			(void) mdsyserror(ep, errno, tname);
391			goto out;
392		}
393	}
394
395	/* append database lines */
396	while (((p = fgets(buf, sizeof (buf), cfp)) != NULL) &&
397	    (confline(buf, &driver, &mnum, &block, &devid_char_p) != 0))
398		;
399	/*
400	 * It is possible to be in a state where the md_devid_destroy flag
401	 * has been set and the mdmonitor service not be enabled on reboot
402	 * such that metadevadm doesn't get run and the entries in mddb.cf
403	 * recreated.  The following checks for this condition and will not
404	 * allow an empty mddb.cf to overwrite md.conf and lose the users
405	 * configuration
406	 */
407	if (check && p == NULL) {
408		(void) mderror(ep, MDE_MDDB_FILE, cname);
409		goto out;
410	}
411
412	for (i = 1; ((p != NULL) && (i <= MDDB_MAX_PATCH)); ++i) {
413		(void) snprintf(line, sizeof (line),
414		    "mddb_bootlist%d=\"%s:%lu:%ld:%s",
415		    i, driver, mnum, block, devid_char_p);
416		if (devid_char_p != NULL) {
417			free(devid_char_p);
418			devid_char_p = NULL;
419		}
420
421		while ((p = fgets(buf, sizeof (buf), cfp)) != NULL) {
422			if (confline(buf, &driver, &mnum, &block,
423			    &devid_char_p) != 0) {
424				continue;
425			}
426			(void) snprintf(entry, sizeof (entry), " %s:%lu:%ld:%s",
427			    driver, mnum, block, devid_char_p);
428
429			if ((strlen(line) + strlen(entry) + 4) > sizeof (line))
430				break;
431			(void) strcat(line, entry);
432			if (devid_char_p != NULL) {
433				free(devid_char_p);
434				devid_char_p = NULL;
435			}
436		}
437		if (verbose)
438			/* CSTYLED */
439			(void) printf("%s\";\n", line);
440		if (doit) {
441			/* CSTYLED */
442			if (fprintf(tfp, "%s\";\n", line) <= 0) {
443				(void) mdsyserror(ep, errno, tname);
444				goto out;
445			}
446		}
447	}
448
449	if (devid_char_p != NULL) {
450		free(devid_char_p);
451		devid_char_p = NULL;
452	}
453
454	/* add trailer */
455	if (verbose)
456		(void) printf("%s\n", ENDMDDBSTR);
457	if (doit) {
458		if (fprintf(tfp, "%s", ENDMDDBSTR) == EOF) {
459			(void) mdsyserror(ep, errno, tname);
460			goto out;
461		}
462	}
463
464	/* close database conf file */
465	if (fclose(cfp) != 0) {
466		cfp = NULL;
467		(void) mdsyserror(ep, errno, cname);
468		goto out;
469	}
470	cfp = NULL;
471
472	/* flush stuff */
473	if (doit) {
474		if ((fflush(tfp) != 0) ||
475		    (fsync(fileno(tfp)) != 0)) {
476			(void) mdsyserror(ep, errno, tname);
477			goto out;
478		}
479	}
480
481	/* return success */
482	return (0);
483
484	/* cleanup, return error */
485out:
486	if (cfp != NULL)
487		(void) fclose(cfp);
488	return (-1);
489}
490