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