s10_support.c revision 11204:153262ed7017
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
56static void s10_err(char *msg, ...) __NORETURN;
57static 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 */
63static boolean_t override = B_FALSE;
64
65static 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*/
75static void
76s10_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
92static int
93s10_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 */
182static char *
183read_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 */
232static int
233get_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 */
280static boolean_t
281have_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 */
316static void
317set_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		if (bit_index >= S10_NUM_EMUL_FEATURES) {
367			/*
368			 * The zone requires emulation that the brand can't
369			 * provide.  Notify the user by displaying an error
370			 * message.
371			 */
372			s10_err(gettext("The zone's version of Solaris 10 is "
373			    "incompatible with the\ncurrent version of the "
374			    "solaris10 brand.\nPlease update your Solaris "
375			    "system to the latest release."));
376		} else {
377			/*
378			 * Set the feature's flag in the bitmap.
379			 */
380			bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
381		}
382	}
383
384	/*
385	 * We're done scanning files.  Set the zone's emulation bitmap.
386	 */
387	(void) closedir(req_emulation_dirp);
388	if ((zoneid = getzoneidbyname(zonename)) < 0)
389		s10_err(gettext("unable to get zoneid"));
390	if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
391		s10_err(gettext("error setting zone's emulation bitmap"));
392}
393
394static void
395fail_xvm()
396{
397	char buf[80];
398
399	if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 &&
400	    strcmp(buf, "i86xpv") == 0 && !override)
401		s10_err(gettext("running the solaris10 brand "
402		    "in a paravirtualized\ndomain is currently not supported"));
403}
404
405static int
406s10_boot(char *zonename)
407{
408	if (!have_valid_ku(zonename))
409		s10_err(gettext("The installed version of Solaris 10 is "
410		    "not supported"));
411
412	set_zone_emul_bitmap(zonename);
413
414	fail_xvm();
415
416	return (0);
417}
418
419static void
420usage()
421{
422	(void) fprintf(stderr, gettext(
423	    "usage:\t%s verify <xml file>\n"
424	    "\t%s boot\n"),
425	    bname, bname);
426	exit(1);
427}
428
429int
430main(int argc, char *argv[])
431{
432	(void) setlocale(LC_ALL, "");
433	(void) textdomain(TEXT_DOMAIN);
434
435	bname = basename(argv[0]);
436
437	if (argc != 3)
438		usage();
439
440	/*
441	 * XXX This is a temporary env variable for the initial release to
442	 * enable the use of features which are not yet tested or fully
443	 * implemented.
444	 */
445	if (getenv("S10BRAND_TEST") != NULL)
446		override = B_TRUE;
447
448	if (strcmp(argv[1], "verify") == 0)
449		return (s10_verify(argv[2]));
450
451	if (strcmp(argv[1], "boot") == 0)
452		return (s10_boot(argv[2]));
453
454	usage();
455	/*NOTREACHED*/
456}
457