xref: /illumos-gate/usr/src/lib/brand/solaris10/s10_support/s10_support.c (revision 1cb875ae88fb9463b368e725c2444776595895cb)
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  * Determine which features/behaviors should be emulated and construct a bitmap
310  * representing the results.  Associate the bitmap with the zone so that
311  * the brand's emulation library will be able to retrieve the bitmap and
312  * determine how the zone's process' behaviors should be emulated.
313  *
314  * This function does not return if an error occurs.
315  */
316 static void
317 set_zone_emul_bitmap(char *zonename)
318 {
319 	char			req_emulation_dir_path[MAXPATHLEN];
320 	DIR			*req_emulation_dirp;
321 	struct dirent		*emul_feature_filep;
322 	char			*filename_endptr;
323 	s10_emul_bitmap_t	bitmap;
324 	unsigned int		bit_index;
325 	zoneid_t		zoneid;
326 
327 	/*
328 	 * If the Solaris 10 directory containing emulation feature files
329 	 * doesn't exist in the zone, then assume that it only needs the
330 	 * most basic emulation and, therefore, doesn't need a bitmap.
331 	 */
332 	if (zone_get_rootpath(zonename, req_emulation_dir_path,
333 	    sizeof (req_emulation_dir_path)) != Z_OK)
334 		s10_err(gettext("error getting zone's path"));
335 	if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR,
336 	    sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path))
337 		s10_err(gettext("error formatting version path"));
338 	if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL)
339 		return;
340 	bzero(bitmap, sizeof (bitmap));
341 
342 	/*
343 	 * Iterate over the contents of the directory and determine which
344 	 * features the brand should emulate for this zone.
345 	 */
346 	while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) {
347 		if (strcmp(emul_feature_filep->d_name, ".") == 0 ||
348 		    strcmp(emul_feature_filep->d_name, "..") == 0)
349 			continue;
350 
351 		/*
352 		 * Convert the file's name to an unsigned integer.  Ignore
353 		 * files whose names aren't unsigned integers.
354 		 */
355 		errno = 0;
356 		bit_index = (unsigned int)strtoul(emul_feature_filep->d_name,
357 		    &filename_endptr, 10);
358 		if (errno != 0 || *filename_endptr != '\0' ||
359 		    filename_endptr == emul_feature_filep->d_name)
360 			continue;
361 
362 		/*
363 		 * Determine if the brand can emulate the feature specified
364 		 * by bit_index.
365 		 */
366 		/*LINTED*/
367 		if (bit_index >= S10_NUM_EMUL_FEATURES) {
368 			/*
369 			 * The zone requires emulation that the brand can't
370 			 * provide.  Notify the user by displaying an error
371 			 * message.
372 			 */
373 			s10_err(gettext("The zone's version of Solaris 10 is "
374 			    "incompatible with the\ncurrent version of the "
375 			    "solaris10 brand.\nPlease update your Solaris "
376 			    "system to the latest release."));
377 		} else {
378 			/*
379 			 * Set the feature's flag in the bitmap.
380 			 */
381 			bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
382 		}
383 	}
384 
385 	/*
386 	 * We're done scanning files.  Set the zone's emulation bitmap.
387 	 */
388 	(void) closedir(req_emulation_dirp);
389 	if ((zoneid = getzoneidbyname(zonename)) < 0)
390 		s10_err(gettext("unable to get zoneid"));
391 	if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
392 		s10_err(gettext("error setting zone's emulation bitmap"));
393 }
394 
395 static void
396 fail_xvm()
397 {
398 	char buf[80];
399 
400 	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 &&
401 	    strcmp(buf, "i86xpv") == 0 && !override)
402 		s10_err(gettext("running the solaris10 brand "
403 		    "in a paravirtualized\ndomain is currently not supported"));
404 }
405 
406 static int
407 s10_boot(char *zonename)
408 {
409 	if (!have_valid_ku(zonename))
410 		s10_err(gettext("The installed version of Solaris 10 is "
411 		    "not supported"));
412 
413 	set_zone_emul_bitmap(zonename);
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