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 	int		i;
284 	char		zonepath[MAXPATHLEN];
285 	char		sanity_skip[MAXPATHLEN];
286 	struct stat64	buf;
287 	char 		*vers_table[] = {
288 			    "141444-09",
289 			    "141445-09",
290 			    NULL};
291 
292 	if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
293 		s10_err(gettext("error getting zone's path"));
294 
295 	/*
296 	 * If the zone was installed to bypass sanity checking for internal
297 	 * testing purposes, just return success.
298 	 */
299 	if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip",
300 	    zonepath) >= sizeof (sanity_skip))
301 		s10_err(gettext("error formating file path"));
302 
303 	if (stat64(sanity_skip, &buf) == 0)
304 		return (B_TRUE);
305 
306 	if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL)
307 		return (B_FALSE);
308 
309 	pstr = patchlist;
310 	while ((p = strtok_r(pstr, " ", &lastp)) != NULL) {
311 		for (i = 0; vers_table[i] != NULL; i++)
312 			if (strcmp(p, vers_table[i]) == 0)
313 				return (B_TRUE);
314 
315 		pstr = NULL;
316 	}
317 
318 	return (B_FALSE);
319 }
320 
321 /*
322  * Determine which features/behaviors should be emulated and construct a bitmap
323  * representing the results.  Associate the bitmap with the zone so that
324  * the brand's emulation library will be able to retrieve the bitmap and
325  * determine how the zone's process' behaviors should be emulated.
326  *
327  * This function does not return if an error occurs.
328  */
329 static void
330 set_zone_emul_bitmap(char *zonename)
331 {
332 	char			req_emulation_dir_path[MAXPATHLEN];
333 	DIR			*req_emulation_dirp;
334 	struct dirent		*emul_feature_filep;
335 	char			*filename_endptr;
336 	s10_emul_bitmap_t	bitmap;
337 	unsigned int		bit_index;
338 	zoneid_t		zoneid;
339 
340 	/*
341 	 * If the Solaris 10 directory containing emulation feature files
342 	 * doesn't exist in the zone, then assume that it only needs the
343 	 * most basic emulation and, therefore, doesn't need a bitmap.
344 	 */
345 	if (zone_get_rootpath(zonename, req_emulation_dir_path,
346 	    sizeof (req_emulation_dir_path)) != Z_OK)
347 		s10_err(gettext("error getting zone's path"));
348 	if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR,
349 	    sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path))
350 		s10_err(gettext("error formatting version path"));
351 	if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL)
352 		return;
353 	bzero(bitmap, sizeof (bitmap));
354 
355 	/*
356 	 * Iterate over the contents of the directory and determine which
357 	 * features the brand should emulate for this zone.
358 	 */
359 	while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) {
360 		if (strcmp(emul_feature_filep->d_name, ".") == 0 ||
361 		    strcmp(emul_feature_filep->d_name, "..") == 0)
362 			continue;
363 
364 		/*
365 		 * Convert the file's name to an unsigned integer.  Ignore
366 		 * files whose names aren't unsigned integers.
367 		 */
368 		errno = 0;
369 		bit_index = (unsigned int)strtoul(emul_feature_filep->d_name,
370 		    &filename_endptr, 10);
371 		if (errno != 0 || *filename_endptr != '\0' ||
372 		    filename_endptr == emul_feature_filep->d_name)
373 			continue;
374 
375 		/*
376 		 * Determine if the brand can emulate the feature specified
377 		 * by bit_index.
378 		 */
379 		if (bit_index >= S10_NUM_EMUL_FEATURES) {
380 			/*
381 			 * The zone requires emulation that the brand can't
382 			 * provide.  Notify the user by displaying an error
383 			 * message.
384 			 */
385 			s10_err(gettext("The zone's version of Solaris 10 is "
386 			    "incompatible with the\ncurrent version of the "
387 			    "solaris10 brand.\nPlease update your Solaris "
388 			    "system to the latest release."));
389 		} else {
390 			/*
391 			 * Set the feature's flag in the bitmap.
392 			 */
393 			bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
394 		}
395 	}
396 
397 	/*
398 	 * We're done scanning files.  Set the zone's emulation bitmap.
399 	 */
400 	(void) closedir(req_emulation_dirp);
401 	if ((zoneid = getzoneidbyname(zonename)) < 0)
402 		s10_err(gettext("unable to get zoneid"));
403 	if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
404 		s10_err(gettext("error setting zone's emulation bitmap"));
405 }
406 
407 static void
408 fail_xvm()
409 {
410 	char buf[80];
411 
412 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 &&
413 	    strcmp(buf, "i86xpv") == 0 && !override)
414 		s10_err(gettext("running the solaris10 brand "
415 		    "in a paravirtualized\ndomain is currently not supported"));
416 }
417 
418 static int
419 s10_boot(char *zonename)
420 {
421 	if (!have_valid_ku(zonename))
422 		s10_err(gettext("The installed version of Solaris 10 is "
423 		    "not supported"));
424 
425 	set_zone_emul_bitmap(zonename);
426 
427 	fail_xvm();
428 
429 	return (0);
430 }
431 
432 static void
433 usage()
434 {
435 	(void) fprintf(stderr, gettext(
436 	    "usage:\t%s verify <xml file>\n"
437 	    "\t%s boot\n"),
438 	    bname, bname);
439 	exit(1);
440 }
441 
442 int
443 main(int argc, char *argv[])
444 {
445 	(void) setlocale(LC_ALL, "");
446 	(void) textdomain(TEXT_DOMAIN);
447 
448 	bname = basename(argv[0]);
449 
450 	if (argc != 3)
451 		usage();
452 
453 	/*
454 	 * XXX This is a temporary env variable for the initial release to
455 	 * enable the use of features which are not yet tested or fully
456 	 * implemented.
457 	 */
458 	if (getenv("S10BRAND_TEST") != NULL)
459 		override = B_TRUE;
460 
461 	if (strcmp(argv[1], "verify") == 0)
462 		return (s10_verify(argv[2]));
463 
464 	if (strcmp(argv[1], "boot") == 0)
465 		return (s10_boot(argv[2]));
466 
467 	usage();
468 	/*NOTREACHED*/
469 }
470