#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <s10_brand.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stropts.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/varargs.h>
#include <unistd.h>
#include <libintl.h>
#include <locale.h>
#include <dirent.h>
#include <sys/systeminfo.h>
#include <libzonecfg.h>
static void s10_err(char *msg, ...) __NORETURN;
static void usage(void) __NORETURN;
static boolean_t override = B_FALSE;
static char *bname = NULL;
#define DELETE_LIST_PATH "/var/sadm/patch/.delete_list"
#define PKGINFO_RD_LEN 128
#define PATCHLIST "PATCHLIST="
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
static void
s10_err(char *msg, ...)
{
char buf[1024];
va_list ap;
va_start(ap, msg);
(void) vsnprintf(buf, sizeof (buf), msg, ap);
va_end(ap);
(void) printf("Error: %s\n", buf);
exit(1);
}
static int
s10_verify(char *xmlfile)
{
zone_dochandle_t handle;
struct zone_devtab devtab;
if ((handle = zonecfg_init_handle()) == NULL)
s10_err(gettext("internal libzonecfg.so.1 error"), 0);
if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
zonecfg_fini_handle(handle);
s10_err(gettext("zonecfg provided an invalid XML file"));
}
if (zonecfg_setdevent(handle) != Z_OK) {
zonecfg_fini_handle(handle);
s10_err(gettext("zonecfg provided an invalid XML file"));
}
if (zonecfg_getdevent(handle, &devtab) == Z_OK) {
if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 &&
!override) {
zonecfg_fini_handle(handle);
s10_err(gettext("solaris10 zones do not currently "
"support /dev/sound"));
}
}
(void) zonecfg_enddevent(handle);
zonecfg_fini_handle(handle);
return (0);
}
static char *
read_pkg_data(FILE *fp)
{
char *start;
char *inp;
char *p;
int char_cnt = 0;
errno = 0;
if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
errno = ENOMEM;
return (NULL);
}
inp = start;
while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
int len;
len = strlen(inp);
if (inp[len - 1] == '\n' &&
(len == 1 || inp[len - 2] != '\\')) {
char_cnt = len;
break;
}
if (inp[len - 1] == '\n' && inp[len - 2] == '\\')
char_cnt += len - 2;
else
char_cnt += PKGINFO_RD_LEN - 1;
if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
errno = ENOMEM;
break;
}
start = p;
inp = start + char_cnt;
}
if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
free(start);
start = NULL;
}
return (start);
}
static int
get_ku_patchlist(char *zonepath, char **patchlist)
{
char pkginfo[MAXPATHLEN];
FILE *fp;
char *buf;
int err = 0;
if (snprintf(pkginfo, sizeof (pkginfo),
"%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath)
>= sizeof (pkginfo))
s10_err(gettext("error formating pkg path"));
if ((fp = fopen(pkginfo, "r")) == NULL)
return (errno);
while ((buf = read_pkg_data(fp)) != NULL) {
if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) {
int len;
len = strlen(buf);
buf[len - 1] = '\0';
if ((*patchlist =
strdup(buf + sizeof (PATCHLIST) - 1)) == NULL)
err = ENOMEM;
free(buf);
break;
}
free(buf);
}
(void) fclose(fp);
return (err);
}
static boolean_t
have_valid_ku(char *zonename)
{
char *p;
char *lastp;
char *pstr;
char *patchlist = NULL;
char zonepath[MAXPATHLEN];
char sanity_skip[MAXPATHLEN];
struct stat64 buf;
boolean_t is_xpv = B_FALSE;
char platform[80];
char *xpv_vers = "142910";
char *vers_table[] = {
"141444-09",
"141445-09"};
if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
s10_err(gettext("error getting zone's path"));
if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip",
zonepath) >= sizeof (sanity_skip))
s10_err(gettext("error formating file path"));
if (stat64(sanity_skip, &buf) == 0)
return (B_TRUE);
if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL)
return (B_FALSE);
if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) != -1 &&
strcmp(platform, "i86xpv") == 0)
is_xpv = B_TRUE;
pstr = patchlist;
while ((p = strtok_r(pstr, " ", &lastp)) != NULL) {
if (is_xpv) {
if (strncmp(p, xpv_vers, 6) == 0)
return (B_TRUE);
} else {
if (strcmp(p, vers_table[0]) == 0 ||
strcmp(p, vers_table[1]) == 0)
return (B_TRUE);
}
pstr = NULL;
}
if (is_xpv)
s10_err(gettext("the zone must have patch 142910 installed "
"when running in a paravirtualized domain"));
return (B_FALSE);
}
static unsigned int
basename_to_uint(const char *basenamep)
{
char *filename_endptr;
unsigned int bit_index;
errno = 0;
bit_index = (unsigned int)strtoul(basenamep, &filename_endptr, 10);
if (errno != 0 || (*filename_endptr != '\n' &&
*filename_endptr != '\0') || filename_endptr == basenamep)
return ((unsigned int)-1);
return (bit_index);
}
static void
set_zone_emul_bitmap(char *zonename)
{
char zoneroot[MAXPATHLEN];
char path[MAXPATHLEN];
DIR *req_emulation_dirp;
struct dirent *emul_feature_filep;
s10_emul_bitmap_t bitmap;
unsigned int bit_index;
zoneid_t zoneid;
FILE *delete_listp;
if (zone_get_rootpath(zonename, zoneroot, sizeof (zoneroot)) != Z_OK)
s10_err(gettext("error getting zone's path"));
if (snprintf(path, sizeof (path), "%s" S10_REQ_EMULATION_DIR,
zoneroot) >= sizeof (path))
s10_err(gettext("zone's emulation versioning directory's path "
"%s" S10_REQ_EMULATION_DIR " is too long"), zoneroot);
if ((req_emulation_dirp = opendir(path)) == NULL)
return;
bzero(bitmap, sizeof (bitmap));
while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) {
if (strcmp(emul_feature_filep->d_name, ".") == 0 ||
strcmp(emul_feature_filep->d_name, "..") == 0)
continue;
bit_index = basename_to_uint(emul_feature_filep->d_name);
if (bit_index == (unsigned int)-1)
continue;
if (bit_index >= S10_NUM_EMUL_FEATURES) {
s10_err(gettext("The zone's version of Solaris 10 is "
"incompatible with the\ncurrent version of the "
"solaris10 brand.\nPlease update your Solaris "
"system to the latest release."));
} else {
bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
}
}
(void) closedir(req_emulation_dirp);
if (snprintf(path, sizeof (path), "%s" DELETE_LIST_PATH, zoneroot) >=
sizeof (path))
s10_err(gettext("zone's delete list's path %s" DELETE_LIST_PATH
" is too long"), zoneroot);
if ((delete_listp = fopen(path, "r")) != NULL) {
while (fgets(path, sizeof (path), delete_listp) != NULL) {
char *const basenamep = path +
sizeof (S10_REQ_EMULATION_DIR);
if (strncmp(path, S10_REQ_EMULATION_DIR,
sizeof (S10_REQ_EMULATION_DIR) - 1) != 0)
continue;
if (*(basenamep - 1) != '/')
continue;
bit_index = basename_to_uint(basenamep);
if (bit_index == (unsigned int)-1)
continue;
if (bit_index < S10_NUM_EMUL_FEATURES)
bitmap[(bit_index >> 3)] &=
~(1 << (bit_index & 0x7));
}
if (ferror(delete_listp) != 0 || feof(delete_listp) == 0)
s10_err(gettext("The program encountered an error while"
" reading from %s" DELETE_LIST_PATH "."), zoneroot);
(void) fclose(delete_listp);
} else if (errno != ENOENT) {
s10_err(gettext("Unable to open %s" DELETE_LIST_PATH ": %s"),
zoneroot, strerror(errno));
}
if ((zoneid = getzoneidbyname(zonename)) < 0)
s10_err(gettext("unable to get zoneid"));
if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
s10_err(gettext("error setting zone's emulation bitmap"));
}
static int
s10_boot(char *zonename)
{
if (!have_valid_ku(zonename))
s10_err(gettext("The installed version of Solaris 10 is "
"not supported"));
set_zone_emul_bitmap(zonename);
return (0);
}
static void
usage()
{
(void) fprintf(stderr, gettext(
"usage:\t%s verify <xml file>\n"
"\t%s boot\n"),
bname, bname);
exit(1);
}
int
main(int argc, char *argv[])
{
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
bname = basename(argv[0]);
if (argc != 3)
usage();
if (getenv("S10BRAND_TEST") != NULL)
override = B_TRUE;
if (strcmp(argv[1], "verify") == 0)
return (s10_verify(argv[2]));
if (strcmp(argv[1], "boot") == 0)
return (s10_boot(argv[2]));
usage();
}