xref: /illumos-gate/usr/src/lib/brand/solaris10/s10_support/s10_support.c (revision 46e5ca4c180bbc8cb48be79bc045e873add461ac)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * s10_support is a small cli utility used to perform some brand-specific
28  * tasks when verifying a zone.  This utility is not intended to be called
29  * by users - it is intended to be invoked by the zones utilities.
30  */
31 
32 #include <ctype.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <libgen.h>
36 #include <limits.h>
37 #include <s10_brand.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <stropts.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/utsname.h>
47 #include <sys/varargs.h>
48 #include <unistd.h>
49 #include <libintl.h>
50 #include <locale.h>
51 #include <dirent.h>
52 #include <sys/systeminfo.h>
53 
54 #include <libzonecfg.h>
55 
56 static void s10_err(char *msg, ...) __NORETURN;
57 static void usage(void) __NORETURN;
58 
59 /*
60  * XXX This is a temporary flag for the initial release to enable the
61  * use of features which are not yet tested or fully implemented.
62  */
63 static boolean_t override = B_FALSE;
64 
65 static char *bname = NULL;
66 
67 #define	PKGINFO_RD_LEN	128
68 #define	PATCHLIST	"PATCHLIST="
69 
70 #if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
71 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
72 #endif
73 
74 /*PRINTFLIKE1*/
75 static void
76 s10_err(char *msg, ...)
77 {
78 	char	buf[1024];
79 	va_list	ap;
80 
81 	va_start(ap, msg);
82 	(void) vsnprintf(buf, sizeof (buf), msg, ap);
83 	va_end(ap);
84 
85 	/* This needs go to stdout so the msgs show up through zoneadm. */
86 	(void) printf("Error: %s\n", buf);
87 
88 	exit(1);
89 	/*NOTREACHED*/
90 }
91 
92 static int
93 s10_verify(char *xmlfile)
94 {
95 	zone_dochandle_t	handle;
96 	struct zone_fstab	fstab;
97 	struct zone_devtab	devtab;
98 	zone_iptype_t		iptype;
99 	struct zone_dstab	dstab;
100 
101 	if ((handle = zonecfg_init_handle()) == NULL)
102 		s10_err(gettext("internal libzonecfg.so.1 error"), 0);
103 
104 	if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
105 		zonecfg_fini_handle(handle);
106 		s10_err(gettext("zonecfg provided an invalid XML file"));
107 	}
108 
109 	/*
110 	 * Check to see whether the zone has any inherit-pkg-dirs
111 	 * configured.
112 	 */
113 	if (zonecfg_setipdent(handle) != Z_OK) {
114 		zonecfg_fini_handle(handle);
115 		s10_err(gettext("zonecfg provided an invalid XML file"));
116 	}
117 	if (zonecfg_getipdent(handle, &fstab) == Z_OK) {
118 		zonecfg_fini_handle(handle);
119 		s10_err(gettext("solaris10 zones do not support "
120 		    "inherit-pkg-dirs"));
121 	}
122 	(void) zonecfg_endipdent(handle);
123 
124 	/*
125 	 * Check to see whether the zone has any unsupported devices
126 	 * configured.
127 	 *
128 	 * The audio framework has changed in Solaris Next as compared to
129 	 * S10.  Data indicates the less than 1/10 of 1 percent of zones
130 	 * are using /dev/sound.  Given the low usage vs. the effort to
131 	 * provide emulation, /dev/sound is currently disallowed.  We can
132 	 * revisit this if there is enough demand.
133 	 */
134 	if (zonecfg_setdevent(handle) != Z_OK) {
135 		zonecfg_fini_handle(handle);
136 		s10_err(gettext("zonecfg provided an invalid XML file"));
137 	}
138 	if (zonecfg_getdevent(handle, &devtab) == Z_OK) {
139 		if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 &&
140 		    !override) {
141 			zonecfg_fini_handle(handle);
142 			s10_err(gettext("solaris10 zones do not currently "
143 			    "support /dev/sound"));
144 		}
145 	}
146 	(void) zonecfg_enddevent(handle);
147 
148 	/*
149 	 * Check to see whether the zone has any experimental features
150 	 * configured.
151 	 */
152 	if (zonecfg_get_iptype(handle, &iptype) == Z_OK &&
153 	    iptype == ZS_EXCLUSIVE && !override) {
154 		zonecfg_fini_handle(handle);
155 		s10_err(gettext("solaris10 zones do not currently support "
156 		    "exclusive ip-type stacks"));
157 	}
158 
159 	if (zonecfg_setdsent(handle) != Z_OK) {
160 		zonecfg_fini_handle(handle);
161 		s10_err(gettext("zonecfg provided an invalid XML file"));
162 	}
163 	if (zonecfg_getdsent(handle, &dstab) == Z_OK && !override) {
164 		zonecfg_fini_handle(handle);
165 		s10_err(gettext("solaris10 zones do not currently support "
166 		    "delegated datasets"));
167 	}
168 	(void) zonecfg_enddsent(handle);
169 
170 	zonecfg_fini_handle(handle);
171 	return (0);
172 }
173 
174 /*
175  * Read an entry from a pkginfo file.  Some of these lines can
176  * either be arbitrarily long or be continued by a backslash at the end of
177  * the line.  This function coalesces lines that are longer than the read
178  * buffer, and lines that are continued, into one buffer which is returned.
179  * The caller must free this memory.  NULL is returned when we hit EOF or
180  * if we run out of memory (errno is set to ENOMEM).
181  */
182 static char *
183 read_pkg_data(FILE *fp)
184 {
185 	char *start;
186 	char *inp;
187 	char *p;
188 	int char_cnt = 0;
189 
190 	errno = 0;
191 	if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
192 		errno = ENOMEM;
193 		return (NULL);
194 	}
195 
196 	inp = start;
197 	while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
198 		int len;
199 
200 		len = strlen(inp);
201 		if (inp[len - 1] == '\n' &&
202 		    (len == 1 || inp[len - 2] != '\\')) {
203 			char_cnt = len;
204 			break;
205 		}
206 
207 		if (inp[len - 1] == '\n' && inp[len - 2] == '\\')
208 			char_cnt += len - 2;
209 		else
210 			char_cnt += PKGINFO_RD_LEN - 1;
211 
212 		if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
213 			errno = ENOMEM;
214 			break;
215 		}
216 
217 		start = p;
218 		inp = start + char_cnt;
219 	}
220 
221 	if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
222 		free(start);
223 		start = NULL;
224 	}
225 
226 	return (start);
227 }
228 
229 /*
230  * Read the SUNWcakr pkginfo file and get the PATCHLIST for the pkg.
231  */
232 static int
233 get_ku_patchlist(char *zonename, char **patchlist)
234 {
235 	char		zonepath[MAXPATHLEN];
236 	char		pkginfo[MAXPATHLEN];
237 	FILE		*fp;
238 	char		*buf;
239 	int		err = 0;
240 
241 	if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
242 		s10_err(gettext("error getting zone's path"));
243 
244 	if (snprintf(pkginfo, sizeof (pkginfo),
245 	    "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath)
246 	    >= sizeof (pkginfo))
247 		s10_err(gettext("error formating pkg path"));
248 
249 	if ((fp = fopen(pkginfo, "r")) == NULL)
250 		return (errno);
251 
252 	while ((buf = read_pkg_data(fp)) != NULL) {
253 		if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) {
254 			int len;
255 
256 			/* remove trailing newline */
257 			len = strlen(buf);
258 			buf[len - 1] = '\0';
259 
260 			if ((*patchlist =
261 			    strdup(buf + sizeof (PATCHLIST) - 1)) == NULL)
262 				err = ENOMEM;
263 
264 			free(buf);
265 			break;
266 		}
267 
268 		free(buf);
269 	}
270 	(void) fclose(fp);
271 
272 	return (err);
273 }
274 
275 /*
276  * Verify that we have the minimum KU needed.
277  * Note that KU patches are accumulative so future KUs will still deliver
278  * 141444 or 141445.
279  */
280 static boolean_t
281 have_valid_ku(char *zonename)
282 {
283 	char		*p;
284 	char		*lastp;
285 	char		*pstr;
286 	char		*patchlist = NULL;
287 	int		i;
288 	char 		*vers_table[] = {
289 			    "141444-09",
290 			    "141445-09",
291 			    NULL};
292 
293 	if (get_ku_patchlist(zonename, &patchlist) != 0 || patchlist == NULL)
294 		return (B_FALSE);
295 
296 	pstr = patchlist;
297 	while ((p = strtok_r(pstr, " ", &lastp)) != NULL) {
298 		for (i = 0; vers_table[i] != NULL; i++)
299 			if (strcmp(p, vers_table[i]) == 0)
300 				return (B_TRUE);
301 
302 		pstr = NULL;
303 	}
304 
305 	return (B_FALSE);
306 }
307 
308 /*
309  * Get the emulation version from the /usr/lib/brand/solaris10/version file
310  * in either the global zone or the non-global zone.
311  */
312 static int
313 get_emul_version_number(char *verspath)
314 {
315 	int	vers = 0;
316 	FILE	*fp;
317 	char	buf[LINE_MAX];
318 
319 	/* If the file doesn't exist, assume version 0 */
320 	if ((fp = fopen(verspath, "r")) == NULL)
321 		return (vers);
322 
323 	while (fgets(buf, sizeof (buf), fp) != NULL) {
324 		if (buf[0] == '#')
325 			continue;
326 
327 		errno = 0;
328 		vers = strtol(buf, (char **)NULL, 10);
329 		if (errno != 0) {
330 			(void) fclose(fp);
331 			s10_err(gettext("error reading minimum version"));
332 		}
333 	}
334 
335 	(void) fclose(fp);
336 
337 	return (vers);
338 }
339 
340 /*
341  * Get the current emulation version that is implemented.
342  */
343 static int
344 get_current_emul_version()
345 {
346 	return (get_emul_version_number("/usr/lib/brand/solaris10/version"));
347 }
348 
349 /*
350  * Get the emulation version that the S10 image requires.  This
351  * reads the optional /usr/lib/brand/solaris10/version file that might
352  * exist on Solaris 10.  That file specifies the minimal solaris10 brand
353  * emulation version that the specific release of S10 requires.  If no
354  * minimal version is specified, the initial emulation remains compatible.
355  *
356  * If a new KU patch is created which needs different handling by the
357  * emulation, then the S10 /usr/lib/brand/solaris10/version file should be
358  * updated to specify a new version.
359  */
360 static int
361 get_image_emul_rqd_version(char *zonename)
362 {
363 	char	zonepath[MAXPATHLEN];
364 	char	verspath[MAXPATHLEN];
365 
366 	if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
367 		s10_err(gettext("error getting zone's path"));
368 
369 	if (snprintf(verspath, sizeof (verspath),
370 	    "%s/root/usr/lib/brand/solaris10/version",
371 	    zonepath) >= sizeof (verspath))
372 		s10_err(gettext("error formating version path"));
373 
374 	return (get_emul_version_number(verspath));
375 }
376 
377 static void
378 fail_xvm()
379 {
380 	char buf[80];
381 
382 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 &&
383 	    strcmp(buf, "i86xpv") == 0 && !override)
384 		s10_err(gettext("running the solaris10 brand "
385 		    "in a paravirtualized\ndomain is currently not supported"));
386 }
387 
388 static int
389 s10_boot(char *zonename)
390 {
391 	zoneid_t zoneid;
392 	int emul_vers;
393 	int rqd_emul_vers;
394 
395 	if (!have_valid_ku(zonename))
396 		s10_err(gettext("The installed version of Solaris 10 is "
397 		    "not supported"));
398 
399 	emul_vers = get_current_emul_version();
400 	rqd_emul_vers = get_image_emul_rqd_version(zonename);
401 
402 	if (rqd_emul_vers > emul_vers)
403 		s10_err(gettext("The zone's version of Solaris 10 is "
404 		    "incompatible with the current version of the solaris10 "
405 		    "brand."));
406 
407 	if ((zoneid = getzoneidbyname(zonename)) < 0)
408 		s10_err(gettext("unable to get zoneid"));
409 
410 	if (zone_setattr(zoneid, S10_EMUL_VERSION_NUM, &rqd_emul_vers,
411 	    sizeof (int)) == -1)
412 		s10_err(gettext("error setting zone's emulation version "
413 		    "property"));
414 
415 	fail_xvm();
416 
417 	return (0);
418 }
419 
420 static void
421 usage()
422 {
423 	(void) fprintf(stderr, gettext(
424 	    "usage:\t%s verify <xml file>\n"
425 	    "\t%s boot\n"),
426 	    bname, bname);
427 	exit(1);
428 }
429 
430 int
431 main(int argc, char *argv[])
432 {
433 	(void) setlocale(LC_ALL, "");
434 	(void) textdomain(TEXT_DOMAIN);
435 
436 	bname = basename(argv[0]);
437 
438 	if (argc != 3)
439 		usage();
440 
441 	/*
442 	 * XXX This is a temporary env variable for the initial release to
443 	 * enable the use of features which are not yet tested or fully
444 	 * implemented.
445 	 */
446 	if (getenv("S10BRAND_TEST") != NULL)
447 		override = B_TRUE;
448 
449 	if (strcmp(argv[1], "verify") == 0)
450 		return (s10_verify(argv[2]));
451 
452 	if (strcmp(argv[1], "boot") == 0)
453 		return (s10_boot(argv[2]));
454 
455 	usage();
456 	/*NOTREACHED*/
457 }
458