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 2010 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 *zonepath, char **patchlist)
234 {
235 	char		pkginfo[MAXPATHLEN];
236 	FILE		*fp;
237 	char		*buf;
238 	int		err = 0;
239 
240 	if (snprintf(pkginfo, sizeof (pkginfo),
241 	    "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath)
242 	    >= sizeof (pkginfo))
243 		s10_err(gettext("error formating pkg path"));
244 
245 	if ((fp = fopen(pkginfo, "r")) == NULL)
246 		return (errno);
247 
248 	while ((buf = read_pkg_data(fp)) != NULL) {
249 		if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) {
250 			int len;
251 
252 			/* remove trailing newline */
253 			len = strlen(buf);
254 			buf[len - 1] = '\0';
255 
256 			if ((*patchlist =
257 			    strdup(buf + sizeof (PATCHLIST) - 1)) == NULL)
258 				err = ENOMEM;
259 
260 			free(buf);
261 			break;
262 		}
263 
264 		free(buf);
265 	}
266 	(void) fclose(fp);
267 
268 	return (err);
269 }
270 
271 /*
272  * Verify that we have the minimum KU needed.
273  * Note that KU patches are accumulative so future KUs will still deliver
274  * 141444 or 141445.
275  */
276 static boolean_t
277 have_valid_ku(char *zonename)
278 {
279 	char		*p;
280 	char		*lastp;
281 	char		*pstr;
282 	char		*patchlist = NULL;
283 	char		zonepath[MAXPATHLEN];
284 	char		sanity_skip[MAXPATHLEN];
285 	struct stat64	buf;
286 	boolean_t	is_xpv = B_FALSE;
287 	char		platform[80];
288 	char		*xpv_vers = "142910";
289 	char 		*vers_table[] = {
290 			    "141444-09",
291 			    "141445-09"};
292 
293 	if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
294 		s10_err(gettext("error getting zone's path"));
295 
296 	/*
297 	 * If the zone was installed to bypass sanity checking for internal
298 	 * testing purposes, just return success.
299 	 */
300 	if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip",
301 	    zonepath) >= sizeof (sanity_skip))
302 		s10_err(gettext("error formating file path"));
303 
304 	if (stat64(sanity_skip, &buf) == 0)
305 		return (B_TRUE);
306 
307 	if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL)
308 		return (B_FALSE);
309 
310 	/*
311 	 * Check if we're running on the i86xpv platform.  If so, the zone
312 	 * needs a different ku patch to work properly.
313 	 */
314 	if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) != -1 &&
315 	    strcmp(platform, "i86xpv") == 0)
316 		is_xpv = B_TRUE;
317 
318 	pstr = patchlist;
319 	while ((p = strtok_r(pstr, " ", &lastp)) != NULL) {
320 		if (is_xpv) {
321 			if (strncmp(p, xpv_vers, 6) == 0)
322 				return (B_TRUE);
323 		} else {
324 			if (strcmp(p, vers_table[0]) == 0 ||
325 			    strcmp(p, vers_table[1]) == 0)
326 				return (B_TRUE);
327 		}
328 
329 		pstr = NULL;
330 	}
331 
332 	if (is_xpv)
333 		s10_err(gettext("the zone must have patch 142910 installed "
334 		    "when running in a paravirtualized domain"));
335 
336 
337 	return (B_FALSE);
338 }
339 
340 /*
341  * Determine which features/behaviors should be emulated and construct a bitmap
342  * representing the results.  Associate the bitmap with the zone so that
343  * the brand's emulation library will be able to retrieve the bitmap and
344  * determine how the zone's process' behaviors should be emulated.
345  *
346  * This function does not return if an error occurs.
347  */
348 static void
349 set_zone_emul_bitmap(char *zonename)
350 {
351 	char			req_emulation_dir_path[MAXPATHLEN];
352 	DIR			*req_emulation_dirp;
353 	struct dirent		*emul_feature_filep;
354 	char			*filename_endptr;
355 	s10_emul_bitmap_t	bitmap;
356 	unsigned int		bit_index;
357 	zoneid_t		zoneid;
358 
359 	/*
360 	 * If the Solaris 10 directory containing emulation feature files
361 	 * doesn't exist in the zone, then assume that it only needs the
362 	 * most basic emulation and, therefore, doesn't need a bitmap.
363 	 */
364 	if (zone_get_rootpath(zonename, req_emulation_dir_path,
365 	    sizeof (req_emulation_dir_path)) != Z_OK)
366 		s10_err(gettext("error getting zone's path"));
367 	if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR,
368 	    sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path))
369 		s10_err(gettext("error formatting version path"));
370 	if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL)
371 		return;
372 	bzero(bitmap, sizeof (bitmap));
373 
374 	/*
375 	 * Iterate over the contents of the directory and determine which
376 	 * features the brand should emulate for this zone.
377 	 */
378 	while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) {
379 		if (strcmp(emul_feature_filep->d_name, ".") == 0 ||
380 		    strcmp(emul_feature_filep->d_name, "..") == 0)
381 			continue;
382 
383 		/*
384 		 * Convert the file's name to an unsigned integer.  Ignore
385 		 * files whose names aren't unsigned integers.
386 		 */
387 		errno = 0;
388 		bit_index = (unsigned int)strtoul(emul_feature_filep->d_name,
389 		    &filename_endptr, 10);
390 		if (errno != 0 || *filename_endptr != '\0' ||
391 		    filename_endptr == emul_feature_filep->d_name)
392 			continue;
393 
394 		/*
395 		 * Determine if the brand can emulate the feature specified
396 		 * by bit_index.
397 		 */
398 		if (bit_index >= S10_NUM_EMUL_FEATURES) {
399 			/*
400 			 * The zone requires emulation that the brand can't
401 			 * provide.  Notify the user by displaying an error
402 			 * message.
403 			 */
404 			s10_err(gettext("The zone's version of Solaris 10 is "
405 			    "incompatible with the\ncurrent version of the "
406 			    "solaris10 brand.\nPlease update your Solaris "
407 			    "system to the latest release."));
408 		} else {
409 			/*
410 			 * Set the feature's flag in the bitmap.
411 			 */
412 			bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
413 		}
414 	}
415 
416 	/*
417 	 * We're done scanning files.  Set the zone's emulation bitmap.
418 	 */
419 	(void) closedir(req_emulation_dirp);
420 	if ((zoneid = getzoneidbyname(zonename)) < 0)
421 		s10_err(gettext("unable to get zoneid"));
422 	if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
423 		s10_err(gettext("error setting zone's emulation bitmap"));
424 }
425 
426 static int
427 s10_boot(char *zonename)
428 {
429 	if (!have_valid_ku(zonename))
430 		s10_err(gettext("The installed version of Solaris 10 is "
431 		    "not supported"));
432 
433 	set_zone_emul_bitmap(zonename);
434 
435 	return (0);
436 }
437 
438 static void
439 usage()
440 {
441 	(void) fprintf(stderr, gettext(
442 	    "usage:\t%s verify <xml file>\n"
443 	    "\t%s boot\n"),
444 	    bname, bname);
445 	exit(1);
446 }
447 
448 int
449 main(int argc, char *argv[])
450 {
451 	(void) setlocale(LC_ALL, "");
452 	(void) textdomain(TEXT_DOMAIN);
453 
454 	bname = basename(argv[0]);
455 
456 	if (argc != 3)
457 		usage();
458 
459 	/*
460 	 * XXX This is a temporary env variable for the initial release to
461 	 * enable the use of features which are not yet tested or fully
462 	 * implemented.
463 	 */
464 	if (getenv("S10BRAND_TEST") != NULL)
465 		override = B_TRUE;
466 
467 	if (strcmp(argv[1], "verify") == 0)
468 		return (s10_verify(argv[2]));
469 
470 	if (strcmp(argv[1], "boot") == 0)
471 		return (s10_boot(argv[2]));
472 
473 	usage();
474 	/*NOTREACHED*/
475 }
476