s10_support.c revision 12734:76969fc28795
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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25/*
26 * s10_support is a small cli utility used to perform some brand-specific
27 * tasks when verifying a zone.  This utility is not intended to be called
28 * by users - it is intended to be invoked by the zones utilities.
29 */
30
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <libgen.h>
35#include <limits.h>
36#include <s10_brand.h>
37#include <stdarg.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <strings.h>
42#include <stropts.h>
43#include <sys/stat.h>
44#include <sys/types.h>
45#include <sys/utsname.h>
46#include <sys/varargs.h>
47#include <unistd.h>
48#include <libintl.h>
49#include <locale.h>
50#include <dirent.h>
51#include <sys/systeminfo.h>
52
53#include <libzonecfg.h>
54
55static void s10_err(char *msg, ...) __NORETURN;
56static void usage(void) __NORETURN;
57
58/*
59 * XXX This is a temporary flag for the initial release to enable the
60 * use of features which are not yet tested or fully implemented.
61 */
62static boolean_t override = B_FALSE;
63
64static char *bname = NULL;
65
66/*
67 * DELETE_LIST_PATH represents the path to a solaris10-branded zone's "delete
68 * list", which is generated by patchrm when it needs to remove files after
69 * the zone reboots.  See set_zone_emul_bitmap() below for additional details.
70 */
71#define	DELETE_LIST_PATH	"/var/sadm/patch/.delete_list"
72
73#define	PKGINFO_RD_LEN	128
74#define	PATCHLIST	"PATCHLIST="
75
76#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
77#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
78#endif
79
80/*PRINTFLIKE1*/
81static void
82s10_err(char *msg, ...)
83{
84	char	buf[1024];
85	va_list	ap;
86
87	va_start(ap, msg);
88	(void) vsnprintf(buf, sizeof (buf), msg, ap);
89	va_end(ap);
90
91	/* This needs go to stdout so the msgs show up through zoneadm. */
92	(void) printf("Error: %s\n", buf);
93
94	exit(1);
95	/*NOTREACHED*/
96}
97
98static int
99s10_verify(char *xmlfile)
100{
101	zone_dochandle_t	handle;
102	struct zone_devtab	devtab;
103	zone_iptype_t		iptype;
104
105	if ((handle = zonecfg_init_handle()) == NULL)
106		s10_err(gettext("internal libzonecfg.so.1 error"), 0);
107
108	if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
109		zonecfg_fini_handle(handle);
110		s10_err(gettext("zonecfg provided an invalid XML file"));
111	}
112
113	/*
114	 * Check to see whether the zone has any unsupported devices
115	 * configured.
116	 *
117	 * The audio framework has changed in Solaris Next as compared to
118	 * S10.  Data indicates the less than 1/10 of 1 percent of zones
119	 * are using /dev/sound.  Given the low usage vs. the effort to
120	 * provide emulation, /dev/sound is currently disallowed.  We can
121	 * revisit this if there is enough demand.
122	 */
123	if (zonecfg_setdevent(handle) != Z_OK) {
124		zonecfg_fini_handle(handle);
125		s10_err(gettext("zonecfg provided an invalid XML file"));
126	}
127	if (zonecfg_getdevent(handle, &devtab) == Z_OK) {
128		if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 &&
129		    !override) {
130			zonecfg_fini_handle(handle);
131			s10_err(gettext("solaris10 zones do not currently "
132			    "support /dev/sound"));
133		}
134	}
135	(void) zonecfg_enddevent(handle);
136
137	/*
138	 * Check to see whether the zone has any experimental features
139	 * configured.
140	 */
141	if (zonecfg_get_iptype(handle, &iptype) == Z_OK &&
142	    iptype == ZS_EXCLUSIVE && !override) {
143		zonecfg_fini_handle(handle);
144		s10_err(gettext("solaris10 zones do not currently support "
145		    "exclusive ip-type stacks"));
146	}
147
148	zonecfg_fini_handle(handle);
149	return (0);
150}
151
152/*
153 * Read an entry from a pkginfo file.  Some of these lines can
154 * either be arbitrarily long or be continued by a backslash at the end of
155 * the line.  This function coalesces lines that are longer than the read
156 * buffer, and lines that are continued, into one buffer which is returned.
157 * The caller must free this memory.  NULL is returned when we hit EOF or
158 * if we run out of memory (errno is set to ENOMEM).
159 */
160static char *
161read_pkg_data(FILE *fp)
162{
163	char *start;
164	char *inp;
165	char *p;
166	int char_cnt = 0;
167
168	errno = 0;
169	if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
170		errno = ENOMEM;
171		return (NULL);
172	}
173
174	inp = start;
175	while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
176		int len;
177
178		len = strlen(inp);
179		if (inp[len - 1] == '\n' &&
180		    (len == 1 || inp[len - 2] != '\\')) {
181			char_cnt = len;
182			break;
183		}
184
185		if (inp[len - 1] == '\n' && inp[len - 2] == '\\')
186			char_cnt += len - 2;
187		else
188			char_cnt += PKGINFO_RD_LEN - 1;
189
190		if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
191			errno = ENOMEM;
192			break;
193		}
194
195		start = p;
196		inp = start + char_cnt;
197	}
198
199	if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
200		free(start);
201		start = NULL;
202	}
203
204	return (start);
205}
206
207/*
208 * Read the SUNWcakr pkginfo file and get the PATCHLIST for the pkg.
209 */
210static int
211get_ku_patchlist(char *zonepath, char **patchlist)
212{
213	char		pkginfo[MAXPATHLEN];
214	FILE		*fp;
215	char		*buf;
216	int		err = 0;
217
218	if (snprintf(pkginfo, sizeof (pkginfo),
219	    "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath)
220	    >= sizeof (pkginfo))
221		s10_err(gettext("error formating pkg path"));
222
223	if ((fp = fopen(pkginfo, "r")) == NULL)
224		return (errno);
225
226	while ((buf = read_pkg_data(fp)) != NULL) {
227		if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) {
228			int len;
229
230			/* remove trailing newline */
231			len = strlen(buf);
232			buf[len - 1] = '\0';
233
234			if ((*patchlist =
235			    strdup(buf + sizeof (PATCHLIST) - 1)) == NULL)
236				err = ENOMEM;
237
238			free(buf);
239			break;
240		}
241
242		free(buf);
243	}
244	(void) fclose(fp);
245
246	return (err);
247}
248
249/*
250 * Verify that we have the minimum KU needed.
251 * Note that KU patches are accumulative so future KUs will still deliver
252 * 141444 or 141445.
253 */
254static boolean_t
255have_valid_ku(char *zonename)
256{
257	char		*p;
258	char		*lastp;
259	char		*pstr;
260	char		*patchlist = NULL;
261	char		zonepath[MAXPATHLEN];
262	char		sanity_skip[MAXPATHLEN];
263	struct stat64	buf;
264	boolean_t	is_xpv = B_FALSE;
265	char		platform[80];
266	char		*xpv_vers = "142910";
267	char 		*vers_table[] = {
268			    "141444-09",
269			    "141445-09"};
270
271	if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
272		s10_err(gettext("error getting zone's path"));
273
274	/*
275	 * If the zone was installed to bypass sanity checking for internal
276	 * testing purposes, just return success.
277	 */
278	if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip",
279	    zonepath) >= sizeof (sanity_skip))
280		s10_err(gettext("error formating file path"));
281
282	if (stat64(sanity_skip, &buf) == 0)
283		return (B_TRUE);
284
285	if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL)
286		return (B_FALSE);
287
288	/*
289	 * Check if we're running on the i86xpv platform.  If so, the zone
290	 * needs a different ku patch to work properly.
291	 */
292	if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) != -1 &&
293	    strcmp(platform, "i86xpv") == 0)
294		is_xpv = B_TRUE;
295
296	pstr = patchlist;
297	while ((p = strtok_r(pstr, " ", &lastp)) != NULL) {
298		if (is_xpv) {
299			if (strncmp(p, xpv_vers, 6) == 0)
300				return (B_TRUE);
301		} else {
302			if (strcmp(p, vers_table[0]) == 0 ||
303			    strcmp(p, vers_table[1]) == 0)
304				return (B_TRUE);
305		}
306
307		pstr = NULL;
308	}
309
310	if (is_xpv)
311		s10_err(gettext("the zone must have patch 142910 installed "
312		    "when running in a paravirtualized domain"));
313
314
315	return (B_FALSE);
316}
317
318/*
319 * Convert the specified file basename into an unsigned integer.  If the
320 * basename contains characters that cannot be converted into digits or the
321 * basename isn't NULL or newline-terminated, then this function returns
322 * the unsigned equivalent of -1.
323 */
324static unsigned int
325basename_to_uint(const char *basenamep)
326{
327	char *filename_endptr;
328	unsigned int bit_index;
329
330	errno = 0;
331	bit_index = (unsigned int)strtoul(basenamep, &filename_endptr, 10);
332	if (errno != 0 || (*filename_endptr != '\n' &&
333	    *filename_endptr != '\0') || filename_endptr == basenamep)
334		return ((unsigned int)-1);
335	return (bit_index);
336}
337
338/*
339 * Determine which features/behaviors should be emulated and construct a bitmap
340 * representing the results.  Associate the bitmap with the zone so that
341 * the brand's emulation library will be able to retrieve the bitmap and
342 * determine how the zone's process' behaviors should be emulated.
343 *
344 * This function does not return if an error occurs.
345 */
346static void
347set_zone_emul_bitmap(char *zonename)
348{
349	char			zoneroot[MAXPATHLEN];
350	char			path[MAXPATHLEN];
351	DIR			*req_emulation_dirp;
352	struct dirent		*emul_feature_filep;
353	s10_emul_bitmap_t	bitmap;
354	unsigned int		bit_index;
355	zoneid_t		zoneid;
356	FILE			*delete_listp;
357
358	/*
359	 * If the Solaris 10 directory containing emulation feature files
360	 * doesn't exist in the zone, then assume that it only needs the
361	 * most basic emulation and, therefore, doesn't need a bitmap.
362	 */
363	if (zone_get_rootpath(zonename, zoneroot, sizeof (zoneroot)) != Z_OK)
364		s10_err(gettext("error getting zone's path"));
365	if (snprintf(path, sizeof (path), "%s" S10_REQ_EMULATION_DIR,
366	    zoneroot) >= sizeof (path))
367		s10_err(gettext("zone's emulation versioning directory's path "
368		    "%s" S10_REQ_EMULATION_DIR " is too long"), zoneroot);
369	if ((req_emulation_dirp = opendir(path)) == NULL)
370		return;
371	bzero(bitmap, sizeof (bitmap));
372
373	/*
374	 * Iterate over the contents of the directory and determine which
375	 * features the brand should emulate for this zone.
376	 */
377	while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) {
378		if (strcmp(emul_feature_filep->d_name, ".") == 0 ||
379		    strcmp(emul_feature_filep->d_name, "..") == 0)
380			continue;
381
382		/*
383		 * Convert the file's name to an unsigned integer.  Ignore
384		 * files whose names aren't unsigned integers.
385		 */
386		bit_index = basename_to_uint(emul_feature_filep->d_name);
387		if (bit_index == (unsigned int)-1)
388			continue;
389
390		/*
391		 * Determine if the brand can emulate the feature specified
392		 * by bit_index.
393		 */
394		if (bit_index >= S10_NUM_EMUL_FEATURES) {
395			/*
396			 * The zone requires emulation that the brand can't
397			 * provide.  Notify the user by displaying an error
398			 * message.
399			 */
400			s10_err(gettext("The zone's version of Solaris 10 is "
401			    "incompatible with the\ncurrent version of the "
402			    "solaris10 brand.\nPlease update your Solaris "
403			    "system to the latest release."));
404		} else {
405			/*
406			 * Set the feature's flag in the bitmap.
407			 */
408			bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
409		}
410	}
411	(void) closedir(req_emulation_dirp);
412
413	/*
414	 * The zone's administrator might have removed a patch that delivered
415	 * an emulation feature file the last time the zone ran.  If so, then
416	 * the zone's patch utilities won't delete the file until the zone's
417	 * svc:/system/patch-finish:delete SMF service runs.  This is
418	 * problematic because the zone will be using system libraries whose
419	 * ioctl structures and syscall invocations will differ from those
420	 * expected by the emulation library.  For example, if an administrator
421	 * removes a patch that affects the formats of MNTFS ioctls, then the
422	 * administrator's zone will use a version of libc.so.1 that issues
423	 * MNTFS ioctls that use older structure versions than the zone's
424	 * emulation library will expect.
425	 *
426	 * Fortunately, the patchrm utility creates a hidden file,
427	 * /var/sadm/patch/.delete_list, which lists all files that
428	 * svc:/system/patch-finish:delete will delete.  We'll determine whether
429	 * this file exists in the zone and disable the emulation bits
430	 * associated with the emulation feature files that will be deleted.
431	 *
432	 * NOTE: The patch tools lofs mount backup copies of critical system
433	 * libraries, such as /lib/libc.so.1, over their replacements whenever
434	 * administrators add or remove DAP patches.  Consequently, there isn't
435	 * a window of vulnerability between patch addition or removal and
436	 * zone reboot.  The aforementioned problem only occurs after a zone
437	 * reboots.
438	 */
439	if (snprintf(path, sizeof (path), "%s" DELETE_LIST_PATH, zoneroot) >=
440	    sizeof (path))
441		s10_err(gettext("zone's delete list's path %s" DELETE_LIST_PATH
442		    " is too long"), zoneroot);
443	if ((delete_listp = fopen(path, "r")) != NULL) {
444		while (fgets(path, sizeof (path), delete_listp) != NULL) {
445			char *const basenamep = path +
446			    sizeof (S10_REQ_EMULATION_DIR);
447
448			/*
449			 * Make sure that the file is in the directory
450			 * containing emulation feature files.  If it is,
451			 * then basenamep should refer to the basename of
452			 * the file.
453			 */
454			if (strncmp(path, S10_REQ_EMULATION_DIR,
455			    sizeof (S10_REQ_EMULATION_DIR) - 1) != 0)
456				continue;
457			if (*(basenamep - 1) != '/')
458				continue;
459
460			/*
461			 * Convert the file's basename into a bit index in
462			 * the emulation bitmap.  If the file's basename isn't
463			 * integral, then skip the file.  Otherwise, clear the
464			 * corresponding bit in the bitmap.
465			 */
466			bit_index = basename_to_uint(basenamep);
467			if (bit_index == (unsigned int)-1)
468				continue;
469			if (bit_index < S10_NUM_EMUL_FEATURES)
470				bitmap[(bit_index >> 3)] &=
471				    ~(1 << (bit_index & 0x7));
472		}
473		if (ferror(delete_listp) != 0 || feof(delete_listp) == 0)
474			s10_err(gettext("The program encountered an error while"
475			    " reading from %s" DELETE_LIST_PATH "."), zoneroot);
476		(void) fclose(delete_listp);
477	} else if (errno != ENOENT) {
478		/*
479		 * The delete list exists but couldn't be opened.  Warn the
480		 * administrator.
481		 */
482		s10_err(gettext("Unable to open %s" DELETE_LIST_PATH ": %s"),
483		    zoneroot, strerror(errno));
484	}
485
486	/*
487	 * We're done scanning files.  Set the zone's emulation bitmap.
488	 */
489	if ((zoneid = getzoneidbyname(zonename)) < 0)
490		s10_err(gettext("unable to get zoneid"));
491	if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
492		s10_err(gettext("error setting zone's emulation bitmap"));
493}
494
495static int
496s10_boot(char *zonename)
497{
498	if (!have_valid_ku(zonename))
499		s10_err(gettext("The installed version of Solaris 10 is "
500		    "not supported"));
501
502	set_zone_emul_bitmap(zonename);
503
504	return (0);
505}
506
507static void
508usage()
509{
510	(void) fprintf(stderr, gettext(
511	    "usage:\t%s verify <xml file>\n"
512	    "\t%s boot\n"),
513	    bname, bname);
514	exit(1);
515}
516
517int
518main(int argc, char *argv[])
519{
520	(void) setlocale(LC_ALL, "");
521	(void) textdomain(TEXT_DOMAIN);
522
523	bname = basename(argv[0]);
524
525	if (argc != 3)
526		usage();
527
528	/*
529	 * XXX This is a temporary env variable for the initial release to
530	 * enable the use of features which are not yet tested or fully
531	 * implemented.
532	 */
533	if (getenv("S10BRAND_TEST") != NULL)
534		override = B_TRUE;
535
536	if (strcmp(argv[1], "verify") == 0)
537		return (s10_verify(argv[2]));
538
539	if (strcmp(argv[1], "boot") == 0)
540		return (s10_boot(argv[2]));
541
542	usage();
543	/*NOTREACHED*/
544}
545