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