# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved. # Use is subject to license terms. # # include library definitions include $(SRC)/lib/Makefile.lib HDRS = instzones_api.h HDRDIR = hdrs # Hammerhead: amd64-only SUBDIRS = $(MACH64) all : TARGET = all install : TARGET = install clean : TARGET = clean clobber : TARGET = clobber _msg : TARGET = _msg .KEEP_STATE: all clean clobber install _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) install_h: $(ROOTHDRS) check: $(CHECKHDRS) FRC: # include library targets include $(SRC)/lib/Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY= libinstzones.a VERS= .1 OBJECTS = \ zones_args.o \ zones_exec.o \ zones_locks.o \ zones_paths.o \ zones_states.o \ zones_str.o \ zones_utils.o \ zones_lofs.o \ zones.o # include library definitions include $(SRC)/lib/Makefile.lib SRCDIR= ../common POFILE = libinstzones.po MSGFILES = $(OBJECTS:%.o=../common/%.i) CLEANFILES += $(MSGFILES) CERRWARN += -Wno-parentheses CERRWARN += -Wno-clobbered CERRWARN += -Wno-address # not linted SMATCH=off LIBS = $(DYNLIB) DYNFLAGS += $(ZLAZYLOAD) # Hammerhead: Add -ldl for dlopen/dlsym/dlclose LDLIBS += -lc -lcontract -lzonecfg -ldl CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(SRCDIR) .KEEP_STATE: all: $(LIBS) $(POFILE): $(MSGFILES) $(BUILDPO.msgfiles) _msg: $(MSGDOMAINPOFILE) # include library targets include $(SRC)/lib/Makefile.targ include $(SRC)/Makefile.msg.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2011 OmniTI Computer Consulting, Inc. All rights reserved. # Use is subject to license terms. # include ../Makefile.com include ../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _INSTZONES_LIB_H #define _INSTZONES_LIB_H /* * Module: instzones_lib.h * Group: libinstzones * Description: This module contains the libinstzones internal data structures, * constants, and function prototypes. This include should not be * needed by any external code (consumers of this library). */ /* * required includes */ /* System includes */ #include #include #include #include /* * C++ prefix */ #ifdef __cplusplus extern "C" { #endif /* constants */ /* macros */ /* * argument array processing type */ /* * This is the "argument array" definition that is returned by _z_new_args * and is used by _z_add_args, _z_free_args, etc. */ struct _argArray_t { long _aaNumArgs; /* number of arguments set */ long _aaMaxArgs; /* number of arguments allocated */ char **_aaArgs; /* actual arguments */ }; typedef struct _argArray_t argArray_t; /* * lock objects */ /* * this allows a root path to be prepended to a lock object; e.g. * rootpath.%s/zone.%s/... */ #define LOBJ_ROOTPATH "rootpath.%s" /* this locks a single zone (zone.name) */ #define LOBJ_ONE_ZONE "zone.%s" /* this locks all zones */ #define LOBJ_ZONEADMIN "zone.*" /* this locks all packages, in all zones */ #define LOBJ_PKGADMIN "zone.*/package.*" #define LOCK_OBJECT_MAXLEN 512 #define LOCK_KEY_MAXLEN 37 /* paths to commands executed by this module */ #define PKGADM_CMD "/usr/bin/pkgadm" #define ZONEADM_CMD "/usr/sbin/zoneadm" /* max message size for program output functions (echo, echo debug, progerr) */ #define MAX_MESSAGE_SIZE 4096 /* maximum number of retries when waiting for lock */ #define MAX_RETRIES 300 /* delay (in seconds) between retries when waiting for lock */ #define RETRY_DELAY_SECS 1 /* Size of buffer increments when reading from pipe */ #define PIPE_BUFFER_INCREMENT 256 /* Maximum number of arguments to pkg_ExecCmdList */ #define MAX_EXEC_CMD_ARGS 100 /* * These dynamic libraries are required in order to use the zones * functionality - if these libraries are not available at runtime, * then zones are assumed to NOT be available, and it is assumed that * the program is running in the global zone with no non-global zones. */ #if defined(LIBZONECFG_PATH) #define ZONECFG1_LIBRARY LIBZONECFG_PATH #else /* defined(LIBZONECFG_PATH) */ #define ZONECFG1_LIBRARY "libzonecfg.so.1" #endif /* defined(LIBZONECFG_PATH) */ #define ZONECFG_LIBRARY "libzonecfg.so" #define CONTRACT1_LIBRARY "libcontract.so.1" #define CONTRACT_LIBRARY "libcontract.so" /* * Environment values used when running commands within a non-global zone */ /* SHELL= */ #define ZONE_FAILSAFESHELL "/sbin/sh" /* PATH= */ #define ZONE_DEF_PATH "/usr/sbin:/usr/bin" /* error codes */ #define ERR_MALLOC_FAIL -50 /* * zone brand list structure */ struct _zoneBrandList { char *string_ptr; struct _zoneBrandList *next; }; /* * zone status structure - used to retrieve and hold status of zones */ typedef unsigned long _zone_status_t; struct _zoneListElement_t { char *_zlName; char *_zlPath; char *_zlScratchName; char *_zlLockObjects; /* * the install "state" refers to the zone states listed in * /usr/include/libzonecfg.h that is stored in the zone_state_t * structure and returned from getzoneent_private() - such as: * ZONE_STATE_CONFIGURED, ZONE_STATE_INCOMPLETE, * ZONE_STATE_INSTALLED, ZONE_STATE_READY, ZONE_STATE_MOUNTED, * ZONE_STATE_SHUTTING_DOWN, ZONE_STATE_DOWN. */ zone_state_t _zlOrigInstallState; zone_state_t _zlCurrInstallState; /* * the kernel "status" refers to the zone status listed in * /usr/include/sys/zone.h, returned by zone_get_state(), * and defined in the zone_status_t enum - such as: * ZONE_IS_UNINITIALIZED, ZONE_IS_READY, ZONE_IS_BOOTING, * ZONE_IS_RUNNING, ZONE_IS_SHUTTING_DOWN, ZONE_IS_EMPTY, * ZONE_IS_DOWN, ZONE_IS_DYING, ZONE_IS_DEAD. */ zone_status_t _zlOrigKernelStatus; zone_status_t _zlCurrKernelStatus; /* * this is an internal state recorded about the zone (ZSF_xxx). */ _zone_status_t _zlStatus; }; typedef struct _zoneListElement_t zoneListElement_t; /* bits used in the _zoneListElement _zlStatus variable */ #define ZST_NOT_BOOTABLE ((_zone_status_t)0x00000001) #define ZST_LOCKED ((_zone_status_t)0x00000002) /* * User-specified list of zones. */ typedef struct zone_spec_s { struct zone_spec_s *zl_next; boolean_t zl_used; char zl_name[ZONENAME_MAX]; } zone_spec_t; /* * The global data structure used to hold all of the global (extern) data * used by this library. * * --> THESE DEFINITIONS ARE ORDER DEPENDENT BASED <-- * --> ON THE ORDER OF THE STRUCTURE INITIALIZERS! <-- */ struct _z_global_data_t { char *_z_ObjectLocks; /* object locks held */ char *_z_root_dir; /* root for zone lib fctns */ int _z_SigReceived; /* received signal count */ pid_t _z_ChildProcessId; /* child to propagate sigs to */ zone_spec_t *_zone_spec; /* zones to operate on */ _z_printf_fcn_t _z_echo; /* operational message fcn */ _z_printf_fcn_t _z_echo_debug; /* debug message fcn */ _z_printf_fcn_t _z_progerr; /* program error fcn */ }; typedef struct _z_global_data_t z_global_data_t; /* * When _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA is defined, * instzones_lib.h will define the z_global_data structure. * Otherwise an extern to the structure is inserted. * * --> THESE DEFINITIONS ARE ORDER DEPENDENT BASED ON <-- * --> THE ORDER OF THE _z_global_data_t STRUCTURE!!! <-- */ #if defined(_INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA) /* define and initialize structure */ z_global_data_t _z_global_data = { NULL, /* *_z_ObjectLocks */ "", /* *_z_root_dir */ 0, /* _z_SigReceived */ -1, /* _z_ChildProcessId */ NULL, /* *_zone_spec */ NULL, /* _z_echo */ NULL, /* _z_echo_debug */ NULL /* _z_progerr */ }; #else /* !defined(_INSTZONES_LIB__Z_DEFINE_GLOBAL_DATA) */ /* define structure extern */ extern z_global_data_t _z_global_data; #endif /* defined(_INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA) */ /* function prototypes */ /* * The following functions can be used by other libs, but not * by applications. */ /* ---> zones_states.c */ boolean_t _z_make_zone_ready(zoneListElement_t *a_zlem); boolean_t _z_make_zone_down(zoneListElement_t *a_zlem); boolean_t _z_make_zone_running(zoneListElement_t *a_zlem); int UmountAllZones(char *mntpnt); void *_z_calloc(size_t size); void *_z_malloc(size_t size); void *_z_realloc(void *ptr, size_t size); void *_z_strdup(char *str); /* ---> zones_utils.c */ /*PRINTFLIKE1*/ void _z_program_error(char *fmt, ...); /*PRINTFLIKE1*/ void _z_echo(char *fmt, ...); /*PRINTFLIKE1*/ void _z_echoDebug(char *a_fmt, ...); int _z_is_directory(char *path); boolean_t _z_running_in_global_zone(void); boolean_t _z_zones_are_implemented(void); void _z_sig_trap(int a_signo); int _z_close_file_descriptors(void *a_fds, int a_fd); boolean_t _z_brands_are_implemented(void); /* ---> zones_locks.c */ boolean_t _z_adjust_lock_object_for_rootpath(char **r_result, char *a_lockObject); boolean_t _z_acquire_lock(char **r_lockKey, char *a_zoneName, char *a_lock, pid_t a_pid, boolean_t a_wait); boolean_t _z_lock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags); boolean_t _z_lock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject, pid_t a_pid, char *a_waitingMsg, char *a_busyMsg); boolean_t _z_release_lock(char *a_zoneName, char *a_lock, char *a_key, boolean_t a_wait); boolean_t _z_unlock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags); boolean_t _z_unlock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject, char *a_errMsg); /* ---> zones_args.c */ void _z_free_args(argArray_t *a_args); argArray_t *_z_new_args(int initialCount); /*PRINTFLIKE2*/ boolean_t _z_add_arg(argArray_t *a_args, char *a_format, ...); int _z_get_argc(argArray_t *a_args); char **_z_get_argv(argArray_t *a_args); /* ---> zones_str.c */ boolean_t _z_strContainsToken(char *a_string, char *a_token, char *a_separators); char *_z_strGetToken(char *r_sep, char *a_string, int a_index, char *a_separators); void _z_strRemoveLeadingWhitespace(char **a_str); void _z_strGetToken_r(char *r_sep, char *a_string, int a_index, char *a_separators, char *a_buf, int a_bufLen); void _z_strAddToken(char **a_old, char *a_new, char a_separator); void _z_strRemoveToken(char **r_string, char *a_token, char *a_separators, int a_index); /*PRINTFLIKE3*/ void _z_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...); /*PRINTFLIKE1*/ char *_z_strPrintf(char *a_format, ...); /* ---> zones_exec.c */ int _z_zone_exec(int *r_status, char **r_results, char *a_inputFile, char *a_path, char *a_argv[], const char *a_zoneName, int *a_fds); int _zexec(const char *a_zoneName, const char *path, char *argv[]); char *_zexec_add_env(char *name, char *value); int _zexec_init_template(void); char **_zexec_prep_env(); /* * C++ postfix */ #ifdef __cplusplus } #endif #endif /* _INSTZONES_LIB_H */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate { global: UmountAllZones; z_brands_are_implemented; z_canoninplace; z_createMountTable; z_create_zone_admin_file; z_destroyMountTable; z_ExecCmdArray; z_ExecCmdList; z_free_brand_list; z_free_zone_list; z_get_nonglobal_zone_list; z_get_nonglobal_zone_list_by_brand; z_get_zonename; z_global_only; z_isPathWritable; z_is_zone_branded; z_is_zone_brand_in_list; z_lock_this_zone; z_lock_zones; z_make_brand_list; z_make_zone_root; z_mount_in_lz; z_non_global_zones_exist; z_on_zone_spec; z_path_canonize; z_resolve_lofs; z_running_in_global_zone; z_set_output_functions; z_set_zone_root; z_set_zone_spec; z_umount_lz_mount; z_unlock_this_zone; z_unlock_zones; z_verify_zone_spec; z_zlist_change_zone_state; z_zlist_get_current_state; z_zlist_get_original_state; z_zlist_get_scratch; z_zlist_get_zonename; z_zlist_get_zonepath; z_zlist_is_zone_runnable; z_zlist_restore_zone_state; z_zone_exec; z_zones_are_implemented; local: *; }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Module: zones.c * Group: libinstzones * Description: Provide "zones" interface for install consolidation code * * Public Methods: * z_create_zone_admin_file - Given a location to create the file, and * optionally an existing administration file, generate an * administration file that can be used to perform "non-interactive" * operations in a non-global zone. * z_free_zone_list - free contents of zoneList_t object * z_get_nonglobal_zone_list - return zoneList_t object describing all * non-global native zones * z_get_nonglobal_zone_list_by_brand - return zoneList_t object describing * all non-global zones matching the list of zone brands passed in. * z_free_brand_list - free contents of a zoneBrandList_t object * z_make_brand_list - return a zoneBrandList_t object describing the list * of all zone brands passed in. * z_get_zonename - return the name of the current zone * z_global_only - Determine if the global zone is only zone on the spec list * z_lock_this_zone - lock this zone * z_lock_zones - lock specified zones * z_mount_in_lz - Mount global zone directory in specified zone's root file * system * z_non_global_zones_exist - Determine if any non-global native zones exist * z_on_zone_spec - Determine if named zone is on the zone_spec list * z_running_in_global_zone - Determine if running in the "global" zone * z_set_output_functions - Link program specific output functions * z_set_zone_root - Set root for zones library operations * z_set_zone_spec - Set list of zones on which actions will be performed * z_umount_lz_mount - Unmount directory mounted with z_mount_in_lz * z_unlock_this_zone - unlock this zone * z_unlock_zones - unlock specified zones * z_verify_zone_spec - Verify list of zones on which actions will be performed * z_zlist_change_zone_state - Change the current state of the specified zone * z_zlist_get_current_state - Determine the current kernel state of the * specified zone * z_zlist_get_original_state - Return the original kernal state of the * specified zone * z_zlist_get_scratch - Determine name of scratch zone * z_zlist_get_zonename - Determine name of specified zone * z_zlist_get_zonepath - Determine zonepath of specified zone * z_zlist_restore_zone_state - Return the zone to the state it was originally * in * z_zone_exec - Execute a Unix command in a specified zone and return results * z_zones_are_implemented - Determine if any zone operations can be performed * z_is_zone_branded - determine if zone has a non-native brand * z_is_zone_brand_in_list - determine if the zone's brand matches the * brand list passed in. * z_brands_are_implemented - determine if branded zones are implemented on * this system */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ /* * When _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA is defined, * instzones_lib.h will define the z_global_data structure. * Otherwise an extern to the structure is inserted. */ #define _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ #define CLUSTER_BRAND_NAME "cluster" /* maximum number of arguments to exec() call */ #define UUID_FORMAT "%02d%02d%02d%03d-%02d%02d%02d%d-%016llx" /* * Library Function Prototypes */ #define streq(a, b) (strcmp((a), (b)) == 0) /* * Local Function Prototypes */ /* * global internal (private) declarations */ /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: z_create_zone_admin_file * Description: Given a location to create the file, and optionally an existing * administration file, generate an administration file that * can be used to perform "non-interactive" operations in a * non-global zone. * Arguments: a_zoneAdminFilename - pointer to string representing the * full path of zone admin file to create * a_userAdminFilename - pointer to string representing the path * to an existing "user" administration file - the * administration file created will contain the * settings contained in this file, modified as * appropriate to supress any interaction; * If this is == NULL then the administration file * created will not contain any extra settings * Returns: boolean_t * == B_TRUE - admin file created * == B_FALSE - failed to create admin file */ boolean_t z_create_zone_admin_file(char *a_zoneAdminFilename, char *a_userAdminFilename) { FILE *zFp; FILE *uFp = (FILE *)NULL; /* entry assertions */ assert(a_zoneAdminFilename != NULL); assert(*a_zoneAdminFilename != '\0'); /* create temporary zone admin file */ zFp = fopen(a_zoneAdminFilename, "w"); if (zFp == (FILE *)NULL) { return (B_FALSE); } /* open user admin file if specified */ if (a_userAdminFilename != (char *)NULL) { uFp = fopen(a_userAdminFilename, "r"); } /* create default admin file for zone pkg ops if no user admin file */ if (uFp == (FILE *)NULL) { /* create default admin file */ (void) fprintf(zFp, "action=nocheck\nauthentication=nocheck\n" "basedir=default\nconflict=nocheck\nidepend=nocheck\n" "instance=unique\npartial=nocheck\nrdepend=nocheck\n" "runlevel=nocheck\nsetuid=nocheck\nspace=nocheck\n" "mail=\n"); } else for (;;) { /* copy user admin file substitute/change appropriate entries */ char buf[LINE_MAX+1]; char *p; /* read next line of user admin file */ p = fgets(buf, sizeof (buf), uFp); if (p == (char *)NULL) { (void) fclose(uFp); break; } /* modify / replace / accept as appropriate */ if (strncmp(buf, "instance=quit", 13) == 0) { (void) fprintf(zFp, "%s", "instance=unique\n"); /*LINTED*/ } else if (strncmp(buf, "keystore=", 9) == 0) { } else if (strncmp(buf, "action=", 7) == 0) { (void) fprintf(zFp, "action=nocheck\n"); } else if (strncmp(buf, "authentication=", 15) == 0) { (void) fprintf(zFp, "authentication=nocheck\n"); } else if (strncmp(buf, "conflict=", 9) == 0) { (void) fprintf(zFp, "conflict=nocheck\n"); } else if (strncmp(buf, "idepend=", 8) == 0) { (void) fprintf(zFp, "idepend=nocheck\n"); } else if (strncmp(buf, "mail=", 5) == 0) { (void) fprintf(zFp, "mail=\n"); } else if (strncmp(buf, "partial=", 8) == 0) { (void) fprintf(zFp, "partial=nocheck\n"); } else if (strncmp(buf, "rdepend=", 8) == 0) { (void) fprintf(zFp, "rdepend=nocheck\n"); } else if (strncmp(buf, "runlevel=", 9) == 0) { (void) fprintf(zFp, "runlevel=nocheck\n"); } else if (strncmp(buf, "setuid=", 7) == 0) { (void) fprintf(zFp, "setuid=nocheck\n"); } else if (strncmp(buf, "space=", 6) == 0) { (void) fprintf(zFp, "space=nocheck\n"); } else { (void) fprintf(zFp, "%s", buf); } } /* close admin file and return success */ (void) fclose(zFp); return (B_TRUE); } /* * Name: z_brands_are_implemented * Description: Determine if any branded zones may be present * Arguments: void * Returns: boolean_t * == B_TRUE - branded zones are supported * == B_FALSE - branded zones are not supported */ boolean_t z_brands_are_implemented(void) { static boolean_t _brandsImplementedDetermined = B_FALSE; static boolean_t _brandsAreImplemented = B_FALSE; /* if availability has not been determined, cache it now */ if (!_brandsImplementedDetermined) { _brandsImplementedDetermined = B_TRUE; _brandsAreImplemented = _z_brands_are_implemented(); if (_brandsAreImplemented) { _z_echoDebug(DBG_BRANDS_ARE_IMPLEMENTED); } else { _z_echoDebug(DBG_BRANDS_NOT_IMPLEMENTED); } } /* return cached answer */ return (_brandsAreImplemented); } /* * Name: z_free_zone_list * Description: free contents of zoneList_t object * Arguments: a_zlst - handle to zoneList_t object to free * Returns: void */ void z_free_zone_list(zoneList_t a_zlst) { int numzones; /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return; } /* free each entry in the zone list */ for (numzones = 0; a_zlst[numzones]._zlName != (char *)NULL; numzones++) { zoneListElement_t *zelm = &a_zlst[numzones]; /* free zone name string */ free(zelm->_zlName); /* free zonepath string */ if (zelm->_zlPath != (char *)NULL) { free(zelm->_zlPath); } } /* free handle to the list */ free(a_zlst); } /* * Name: z_get_nonglobal_zone_list * Description: return zoneList_t object describing all non-global * native zones - branded zones are not included in list * Arguments: None. * Returns: zoneList_t * == NULL - error, list could not be generated * != NULL - success, list returned * NOTE: Any zoneList_t returned is placed in new storage for the * calling function. The caller must use 'z_free_zone_list' to * dispose of the storage once the list is no longer needed. */ zoneList_t z_get_nonglobal_zone_list(void) { zoneList_t zones; zoneBrandList_t *brands = NULL; if ((brands = z_make_brand_list("native cluster", " ")) == NULL) return (NULL); zones = z_get_nonglobal_zone_list_by_brand(brands); z_free_brand_list(brands); return (zones); } /* * Name: z_free_brand_list * Description: Free contents of zoneBrandList_t object * Arguments: brands - pointer to zoneBrandList_t object to free * Returns: void */ void z_free_brand_list(zoneBrandList_t *brands) { while (brands != NULL) { zoneBrandList_t *temp = brands; free(brands->string_ptr); brands = brands->next; free(temp); } } /* * Name: z_make_brand_list * Description: Given a string with a list of brand name delimited by * the delimeter passed in, build a zoneBrandList_t structure * with the list of brand names and return it to the caller. * Arguments: * brands - const char pointer to string list of brand names * delim - const char pointer to string representing the * delimeter for brands string. * Returns: zoneBrandList_t * * == NULL - error, list could not be generated * != NULL - success, list returned * NOTE: Any zoneBrandList_t returned is placed in new storage for the * calling function. The caller must use 'z_free_brand_list' to * dispose of the storage once the list is no longer needed. */ zoneBrandList_t * z_make_brand_list(const char *brands, const char *delim) { zoneBrandList_t *brand = NULL, *head = NULL; char *blist = NULL; char *str = NULL; if ((blist = strdup(brands)) == NULL) return (NULL); if ((str = strtok(blist, delim)) != NULL) { if ((brand = (zoneBrandList_t *) malloc(sizeof (struct _zoneBrandList))) == NULL) { return (NULL); } head = brand; brand->string_ptr = strdup(str); brand->next = NULL; while ((str = strtok(NULL, delim)) != NULL) { if ((brand->next = (zoneBrandList_t *) malloc(sizeof (struct _zoneBrandList))) == NULL) { return (NULL); } brand = brand->next; brand->string_ptr = strdup(str); brand->next = NULL; } } free(blist); return (head); } /* * Name: z_get_nonglobal_zone_list_by_brand * Description: return zoneList_t object describing all non-global * zones matching the list of brands passed in. * Arguments: brands - The list of zone brands to look for. * Returns: zoneList_t * == NULL - error, list could not be generated * != NULL - success, list returned * NOTE: Any zoneList_t returned is placed in new storage for the * calling function. The caller must use 'z_free_zone_list' to * dispose of the storage once the list is no longer needed. */ zoneList_t z_get_nonglobal_zone_list_by_brand(zoneBrandList_t *brands) { FILE *zoneIndexFP; int numzones = 0; struct zoneent *ze; zoneList_t zlst = NULL; FILE *mapFP; char zonename[ZONENAME_MAX]; zone_spec_t *zent; /* if zones are not implemented, return empty list */ if (!z_zones_are_implemented()) { return ((zoneList_t)NULL); } /* * Open the zone index file. Note that getzoneent_private() handles * NULL. */ zoneIndexFP = setzoneent(); mapFP = zonecfg_open_scratch("", B_FALSE); /* index file open; scan all zones; see if any are at least installed */ while ((ze = getzoneent_private(zoneIndexFP)) != NULL) { zone_state_t st; /* skip the global zone */ if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0) { free(ze); continue; } /* * skip any zones with brands not on the brand list */ if (!z_is_zone_brand_in_list(ze->zone_name, brands)) { free(ze); continue; } /* * If the user specified an explicit zone list, then ignore any * zones that aren't on that list. */ if ((zent = _z_global_data._zone_spec) != NULL) { while (zent != NULL) { if (strcmp(zent->zl_name, ze->zone_name) == 0) break; zent = zent->zl_next; } if (zent == NULL) { free(ze); continue; } } /* non-global zone: create entry for this zone */ if (numzones == 0) { zlst = (zoneList_t)_z_calloc( sizeof (zoneListElement_t)*2); } else { zlst = (zoneList_t)_z_realloc(zlst, sizeof (zoneListElement_t)*(numzones+2)); (void) memset(&zlst[numzones], 0L, sizeof (zoneListElement_t)*2); } /* * remember the zone name, zonepath and the current * zone state of the zone. */ zlst[numzones]._zlName = _z_strdup(ze->zone_name); zlst[numzones]._zlPath = _z_strdup(ze->zone_path); zlst[numzones]._zlOrigInstallState = ze->zone_state; zlst[numzones]._zlCurrInstallState = ze->zone_state; /* get the zone kernel status */ if (zone_get_state(ze->zone_name, &st) != Z_OK) { st = ZONE_STATE_INCOMPLETE; } _z_echoDebug(DBG_ZONES_NGZ_LIST_STATES, ze->zone_name, ze->zone_state, st); /* * For a scratch zone, we need to know the kernel zone name. */ if (zonecfg_in_alt_root() && mapFP != NULL && zonecfg_find_scratch(mapFP, ze->zone_name, zonecfg_get_root(), zonename, sizeof (zonename)) != -1) { free(zlst[numzones]._zlScratchName); zlst[numzones]._zlScratchName = _z_strdup(zonename); } /* * remember the current kernel status of the zone. */ zlst[numzones]._zlOrigKernelStatus = st; zlst[numzones]._zlCurrKernelStatus = st; numzones++; free(ze); } /* close the index file */ endzoneent(zoneIndexFP); if (mapFP != NULL) zonecfg_close_scratch(mapFP); /* return generated list */ return (zlst); } /* * Name: z_get_zonename * Description: return the name of the current zone * Arguments: void * Returns: char * * - pointer to string representing the name of the current * zone * NOTE: Any string returned is placed in new storage for the * calling function. The caller must use 'Free' to dispose * of the storage once the string is no longer needed. */ char * z_get_zonename(void) { ssize_t zonenameLen; char zonename[ZONENAME_MAX]; zoneid_t zoneid = (zoneid_t)-1; /* if zones are not implemented, return "" */ if (!z_zones_are_implemented()) { return (_z_strdup("")); } /* get the zone i.d. of the current zone */ zoneid = getzoneid(); /* get the name of the current zone */ zonenameLen = getzonenamebyid(zoneid, zonename, sizeof (zonename)); /* return "" if could not get zonename */ if (zonenameLen < 1) { return (_z_strdup("")); } return (_z_strdup(zonename)); } /* * Name: z_global_only * Description: Determine if the global zone is only zone on the spec list. * Arguments: None * Returns: B_TRUE if global zone is the only zone on the list, * B_FALSE otherwise. */ boolean_t z_global_only(void) { /* return true if zones are not implemented - treate as global zone */ if (!z_zones_are_implemented()) { return (B_TRUE); } /* return true if this is the global zone */ if (_z_global_data._zone_spec != NULL && _z_global_data._zone_spec->zl_next == NULL && strcmp(_z_global_data._zone_spec->zl_name, GLOBAL_ZONENAME) == 0) { return (B_TRUE); } /* return false - not the global zone */ return (B_FALSE); } /* * Name: z_lock_this_zone * Description: lock this zone * Arguments: a_lflags - [RO, *RO] - (ZLOCKS_T) * Flags indicating which locks to acquire * Returns: boolean_t * == B_TRUE - success specified locks acquired * == B_FALSE - failure specified locks not acquired * NOTE: the lock objects for "this zone" are maintained internally. */ boolean_t z_lock_this_zone(ZLOCKS_T a_lflags) { boolean_t b; char *zoneName; pid_t pid = (pid_t)0; /* entry assertions */ assert(a_lflags != ZLOCKS_NONE); /* entry debugging info */ _z_echoDebug(DBG_ZONES_LCK_THIS, a_lflags); zoneName = z_get_zonename(); pid = getpid(); /* lock zone administration */ if (a_lflags & ZLOCKS_ZONE_ADMIN) { b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks, zoneName, LOBJ_ZONEADMIN, pid, MSG_ZONES_LCK_THIS_ZONEADM, ERR_ZONES_LCK_THIS_ZONEADM); if (!b) { (void) free(zoneName); return (B_FALSE); } } /* lock package administration always */ if (a_lflags & ZLOCKS_PKG_ADMIN) { b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks, zoneName, LOBJ_PKGADMIN, pid, MSG_ZONES_LCK_THIS_PKGADM, ERR_ZONES_LCK_THIS_PKGADM); if (!b) { (void) z_unlock_this_zone(a_lflags); (void) free(zoneName); return (B_FALSE); } } (void) free(zoneName); return (B_TRUE); } /* * Name: z_lock_zones * Description: lock specified zones * Arguments: a_zlst - zoneList_t object describing zones to lock * a_lflags - [RO, *RO] - (ZLOCKS_T) * Flags indicating which locks to acquire * Returns: boolean_t * == B_TRUE - success, zones locked * == B_FALSE - failure, zones not locked */ boolean_t z_lock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags) { boolean_t b; int i; /* entry assertions */ assert(a_lflags != ZLOCKS_NONE); /* entry debugging info */ _z_echoDebug(DBG_ZONES_LCK_ZONES, a_lflags); /* if zones are not implemented, return TRUE */ if (z_zones_are_implemented() == B_FALSE) { _z_echoDebug(DBG_ZONES_LCK_ZONES_UNIMP); return (B_TRUE); } /* lock this zone first before locking other zones */ b = z_lock_this_zone(a_lflags); if (b == B_FALSE) { return (b); } /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { _z_echoDebug(DBG_ZONES_LCK_ZONES_NOZONES); return (B_FALSE); } /* zones exist */ _z_echoDebug(DBG_ZONES_LCK_ZONES_EXIST); /* * lock each listed zone that is currently running */ for (i = 0; (a_zlst[i]._zlName != (char *)NULL); i++) { /* ignore zone if already locked */ if (a_zlst[i]._zlStatus & ZST_LOCKED) { continue; } /* ignore zone if not running */ if (a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_RUNNING && a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_MOUNTED) { continue; } /* * mark zone locked - if interrupted out during lock, an attempt * will be made to release the lock */ a_zlst[i]._zlStatus |= ZST_LOCKED; /* lock this zone */ b = _z_lock_zone(&a_zlst[i], a_lflags); /* on failure unlock all zones and return error */ if (b != B_TRUE) { _z_program_error(ERR_ZONES_LCK_ZONES_FAILED, a_zlst[i]._zlName); (void) z_unlock_zones(a_zlst, a_lflags); return (B_FALSE); } } /* success */ return (B_TRUE); } /* * Name: z_mount_in_lz * Description: Mount global zone directory in specified zone's root file system * Arguments: r_lzMountPoint - pointer to handle to string - on success, the * full path to the mount point relative to the global zone * root file system is returned here - this is needed to * unmount the directory when it is no longer needed * r_lzRootPath - pointer to handle to string - on success, the * full path to the mount point relative to the specified * zone's root file system is returned here - this is * passed to any command executing in the specified zone to * access the directory mounted * a_zoneName - pointer to string representing the name of the zone * to mount the specified global zone directory in * a_gzPath - pointer to string representing the full absolute path * of the global zone directory to LOFS mount inside of the * specified non-global zone * a_mountPointPrefix - pointer to string representing the prefix * to be used when creating the mount point name in the * specified zone's root directory * Returns: boolean_t * == B_TRUE - global zone directory mounted successfully * == B_FALSE - failed to mount directory in specified zone * NOTE: Any strings returned is placed in new storage for the * calling function. The caller must use 'Free' to dispose * of the storage once the strings are no longer needed. */ boolean_t z_mount_in_lz(char **r_lzMountPoint, char **r_lzRootPath, char *a_zoneName, char *a_gzPath, char *a_mountPointPrefix) { char lzRootPath[MAXPATHLEN] = {'\0'}; char uuid[MAXPATHLEN] = {'\0'}; char gzMountPoint[MAXPATHLEN] = {'\0'}; char lzMountPoint[MAXPATHLEN] = {'\0'}; hrtime_t hretime; int err; int slen; struct tm tstruct; time_t thetime; zoneid_t zid; /* entry assertions */ assert(a_zoneName != (char *)NULL); assert(*a_zoneName != '\0'); assert(a_gzPath != (char *)NULL); assert(*a_gzPath != '\0'); assert(r_lzMountPoint != (char **)NULL); assert(r_lzRootPath != (char **)NULL); /* entry debugging info */ _z_echoDebug(DBG_ZONES_MOUNT_IN_LZ_ENTRY, a_zoneName, a_gzPath); /* reset returned non-global zone mount point path handle */ *r_lzMountPoint = (char *)NULL; *r_lzRootPath = (char *)NULL; /* if zones are not implemented, return FALSE */ if (z_zones_are_implemented() == B_FALSE) { return (B_FALSE); } /* error if global zone path is not absolute */ if (*a_gzPath != '/') { _z_program_error(ERR_GZPATH_NOT_ABSOLUTE, a_gzPath); return (B_FALSE); } /* error if global zone path does not exist */ if (_z_is_directory(a_gzPath) != 0) { _z_program_error(ERR_GZPATH_NOT_DIR, a_gzPath, strerror(errno)); return (B_FALSE); } /* verify that specified non-global zone exists */ err = zone_get_id(a_zoneName, &zid); if (err != Z_OK) { _z_program_error(ERR_GET_ZONEID, a_zoneName, zonecfg_strerror(err)); return (B_FALSE); } /* obtain global zone path to non-global zones root file system */ err = zone_get_rootpath(a_zoneName, lzRootPath, sizeof (lzRootPath)); if (err != Z_OK) { _z_program_error(ERR_NO_ZONE_ROOTPATH, a_zoneName, zonecfg_strerror(err)); return (B_FALSE); } if (lzRootPath[0] == '\0') { _z_program_error(ERR_ROOTPATH_EMPTY, a_zoneName); return (B_FALSE); } /* * lofs resolve the non-global zone's root path first in case * its in a path that's been lofs mounted read-only. */ z_resolve_lofs(lzRootPath, sizeof (lzRootPath)); /* verify that the root path exists */ if (_z_is_directory(lzRootPath) != 0) { _z_program_error(ERR_LZROOT_NOTDIR, lzRootPath, strerror(errno)); return (B_FALSE); } /* * generate a unique key - the key is the same length as unique uid * but contains different information that is as unique as can be made; * include current hires time (nanosecond real timer). Such a unique * i.d. will look like: * 0203104092-1145345-0004e94d6af481a0 */ hretime = gethrtime(); thetime = time((time_t *)NULL); (void) localtime_r(&thetime, &tstruct); slen = snprintf(uuid, sizeof (uuid), UUID_FORMAT, tstruct.tm_mday, tstruct.tm_mon, tstruct.tm_year, tstruct.tm_yday, tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec, tstruct.tm_wday, hretime); if (slen > sizeof (uuid)) { _z_program_error(ERR_GZMOUNT_SNPRINTFUUID_FAILED, UUID_FORMAT, sizeof (uuid)); return (B_FALSE); } /* create the global zone mount point */ slen = snprintf(gzMountPoint, sizeof (gzMountPoint), "%s/.SUNW_%s_%s", lzRootPath, a_mountPointPrefix ? a_mountPointPrefix : "zones", uuid); if (slen > sizeof (gzMountPoint)) { _z_program_error(ERR_GZMOUNT_SNPRINTFGMP_FAILED, "%s/.SUNW_%s_%s", lzRootPath, a_mountPointPrefix ? a_mountPointPrefix : "zones", uuid, sizeof (gzMountPoint)); return (B_FALSE); } slen = snprintf(lzMountPoint, sizeof (lzMountPoint), "%s", gzMountPoint+strlen(lzRootPath)); if (slen > sizeof (lzMountPoint)) { _z_program_error(ERR_GZMOUNT_SNPRINTFLMP_FAILED, "%s", gzMountPoint+strlen(lzRootPath), sizeof (lzMountPoint)); return (B_FALSE); } _z_echoDebug(DBG_MNTPT_NAMES, a_gzPath, a_zoneName, gzMountPoint, lzMountPoint); /* error if the mount point already exists */ if (_z_is_directory(gzMountPoint) == 0) { _z_program_error(ERR_ZONEROOT_NOTDIR, gzMountPoint, a_zoneName, strerror(errno)); return (B_FALSE); } /* create the temporary mount point */ if (mkdir(gzMountPoint, 0600) != 0) { _z_program_error(ERR_MNTPT_MKDIR, gzMountPoint, a_zoneName, strerror(errno)); return (B_FALSE); } /* mount the global zone path on the non-global zone root file system */ err = mount(a_gzPath, gzMountPoint, MS_RDONLY|MS_DATA, "lofs", (char *)NULL, 0, (char *)NULL, 0); if (err != 0) { _z_program_error(ERR_GZMOUNT_FAILED, a_gzPath, gzMountPoint, a_zoneName, strerror(errno)); return (B_FALSE); } /* success - return both mountpoints to caller */ *r_lzMountPoint = _z_strdup(gzMountPoint); *r_lzRootPath = _z_strdup(lzMountPoint); /* return success */ return (B_TRUE); } /* * Name: z_non_global_zones_exist * Description: Determine if any non-global native zones exist * Arguments: None. * Returns: boolean_t * == B_TRUE - at least one non-global native zone exists * == B_FALSE - no non-global native zone exists */ boolean_t z_non_global_zones_exist(void) { FILE *zoneIndexFP; boolean_t anyExist = B_FALSE; struct zoneent *ze; zone_spec_t *zent; /* if zones are not implemented, return FALSE */ if (z_zones_are_implemented() == B_FALSE) { return (B_FALSE); } /* determine if any zones are configured */ zoneIndexFP = setzoneent(); if (zoneIndexFP == NULL) { return (B_FALSE); } /* index file open; scan all zones; see if any are at least installed */ while ((ze = getzoneent_private(zoneIndexFP)) != NULL) { /* * If the user specified an explicit zone list, then ignore any * zones that aren't on that list. */ if ((zent = _z_global_data._zone_spec) != NULL) { while (zent != NULL) { if (strcmp(zent->zl_name, ze->zone_name) == 0) break; zent = zent->zl_next; } if (zent == NULL) { free(ze); continue; } } /* skip the global zone */ if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0) { free(ze); continue; } /* skip any branded zones */ if (z_is_zone_branded(ze->zone_name)) { free(ze); continue; } /* is this zone installed? */ if (ze->zone_state >= ZONE_STATE_INSTALLED) { free(ze); anyExist = B_TRUE; break; } free(ze); } /* close the index file */ endzoneent(zoneIndexFP); /* return results */ return (anyExist); } /* * Name: z_on_zone_spec * Description: Determine if named zone is on the zone_spec list. * Arguments: Pointer to name to test. * Returns: B_TRUE if named zone is on the list or if the user specified * no list at all (all zones is the default), B_FALSE otherwise. */ boolean_t z_on_zone_spec(const char *zonename) { zone_spec_t *zent; /* entry assertions */ assert(zonename != NULL); assert(*zonename != '\0'); /* return true if zones not implemented or no zone spec list defined */ if (!z_zones_are_implemented() || _z_global_data._zone_spec == NULL) { return (B_TRUE); } /* return true if named zone is on the zone spec list */ for (zent = _z_global_data._zone_spec; zent != NULL; zent = zent->zl_next) { if (strcmp(zent->zl_name, zonename) == 0) return (B_TRUE); } /* named zone is not on the zone spec list */ return (B_FALSE); } /* * Name: z_running_in_global_zone * Description: Determine if running in the "global" zone * Arguments: void * Returns: boolean_t * == B_TRUE - running in global zone * == B_FALSE - not running in global zone */ boolean_t z_running_in_global_zone(void) { static boolean_t _zoneIdDetermined = B_FALSE; static boolean_t _zoneIsGlobal = B_FALSE; /* if ID has not been determined, cache it now */ if (!_zoneIdDetermined) { _zoneIdDetermined = B_TRUE; _zoneIsGlobal = _z_running_in_global_zone(); } return (_zoneIsGlobal); } /* * Name: z_set_output_functions * Description: Link program specific output functions to this library. * Arguments: a_echo_fcn - (_z_printf_fcn_t) * Function to call to cause "normal operation" messages * to be output/displayed * a_echo_debug_fcn - (_z_printf_fcn_t) * Function to call to cause "debugging" messages * to be output/displayed * a_progerr_fcn - (_z_printf_fcn_t) * Function to call to cause "program error" messages * to be output/displayed * Returns: void * NOTE: If NULL is specified for any function, then the functionality * associated with that function is disabled. * NOTE: The function pointers provided must call a function that * takes two arguments: * function(char *format, char *message) * Any registered function will be called like: * function("%s", "message") */ void z_set_output_functions(_z_printf_fcn_t a_echo_fcn, _z_printf_fcn_t a_echo_debug_fcn, _z_printf_fcn_t a_progerr_fcn) { _z_global_data._z_echo = a_echo_fcn; _z_global_data._z_echo_debug = a_echo_debug_fcn; _z_global_data._z_progerr = a_progerr_fcn; } /* * Name: z_set_zone_root * Description: Set root for zones library operations * Arguments: Path to root of boot environment containing zone; must be * absolute. * Returns: None. * NOTE: Must be called before performing any zone-related operations. * (Currently called directly by set_inst_root() during -R * argument handling.) */ void z_set_zone_root(const char *zroot) { char *rootdir; /* if zones are not implemented, just return */ if (!z_zones_are_implemented()) return; /* entry assertions */ assert(zroot != NULL); rootdir = _z_strdup((char *)zroot); z_canoninplace(rootdir); if (strcmp(rootdir, "/") == 0) { rootdir[0] = '\0'; } /* free any existing cached root path */ if (*_z_global_data._z_root_dir != '\0') { free(_z_global_data._z_root_dir); _z_global_data._z_root_dir = NULL; } /* store duplicate of new zone root path */ if (*rootdir != '\0') { _z_global_data._z_root_dir = _z_strdup(rootdir); } else { _z_global_data._z_root_dir = ""; } /* set zone root path */ zonecfg_set_root(rootdir); free(rootdir); } /* * Name: z_set_zone_spec * Description: Set list of zones on which actions will be performed. * Arguments: Whitespace-separated list of zone names. * Returns: 0 on success, -1 on error. * NOTES: Will call _z_program_error if argument can't be parsed or * memory not available. */ int z_set_zone_spec(const char *zlist) { const char *zend; ptrdiff_t zlen; zone_spec_t *zent; zone_spec_t *zhead; zone_spec_t **znextp = &zhead; /* entry assertions */ assert(zlist != NULL); /* parse list to zone_spec_t list, store in global data */ for (;;) { while (isspace(*zlist)) { zlist++; } if (*zlist == '\0') { break; } for (zend = zlist; *zend != '\0'; zend++) { if (isspace(*zend)) { break; } } zlen = ((ptrdiff_t)zend) - ((ptrdiff_t)zlist); if (zlen >= ZONENAME_MAX) { _z_program_error(ERR_ZONE_NAME_ILLEGAL, zlen, zlist); return (-1); } zent = _z_malloc(sizeof (*zent)); (void) memcpy(zent->zl_name, zlist, zlen); zent->zl_name[zlen] = '\0'; zent->zl_used = B_FALSE; *znextp = zent; znextp = &zent->zl_next; zlist = zend; } *znextp = NULL; if (zhead == NULL) { _z_program_error(ERR_ZONE_LIST_EMPTY); return (-1); } _z_global_data._zone_spec = zhead; return (0); } /* * Name: z_umount_lz_mount * Description: Unmount directory mounted with z_mount_in_lz * Arguments: a_lzMountPointer - pointer to string returned by z_mount_in_lz * Returns: boolean_t * == B_TRUE - successfully unmounted directory * == B_FALSE - failed to unmount directory */ boolean_t z_umount_lz_mount(char *a_lzMountPoint) { int err; /* entry assertions */ assert(a_lzMountPoint != (char *)NULL); assert(*a_lzMountPoint != '\0'); /* entry debugging info */ _z_echoDebug(DBG_ZONES_UNMOUNT_FROM_LZ_ENTRY, a_lzMountPoint); /* if zones are not implemented, return TRUE */ if (z_zones_are_implemented() == B_FALSE) { return (B_FALSE); } /* error if global zone path is not absolute */ if (*a_lzMountPoint != '/') { _z_program_error(ERR_LZMNTPT_NOT_ABSOLUTE, a_lzMountPoint); return (B_FALSE); } /* verify mount point exists */ if (_z_is_directory(a_lzMountPoint) != 0) { _z_program_error(ERR_LZMNTPT_NOTDIR, a_lzMountPoint, strerror(errno)); return (B_FALSE); } /* unmount */ err = umount2(a_lzMountPoint, 0); if (err != 0) { _z_program_error(ERR_GZUMOUNT_FAILED, a_lzMountPoint, strerror(errno)); return (B_FALSE); } /* remove the mount point */ (void) remove(a_lzMountPoint); /* return success */ return (B_TRUE); } /* * Name: z_unlock_this_zone * Description: unlock this zone * Arguments: a_lflags - [RO, *RO] - (ZLOCKS_T) * Flags indicating which locks to release * Returns: boolean_t * == B_TRUE - success specified locks released * == B_FALSE - failure specified locks may not be released * NOTE: the lock objects for "this zone" are maintained internally. */ boolean_t z_unlock_this_zone(ZLOCKS_T a_lflags) { boolean_t b; boolean_t errors = B_FALSE; char *zoneName; /* entry assertions */ assert(a_lflags != ZLOCKS_NONE); /* entry debugging info */ _z_echoDebug(DBG_ZONES_ULK_THIS, a_lflags); /* return if no objects locked */ if ((_z_global_data._z_ObjectLocks == (char *)NULL) || (*_z_global_data._z_ObjectLocks == '\0')) { return (B_TRUE); } zoneName = z_get_zonename(); /* unlock package administration */ if (a_lflags & ZLOCKS_PKG_ADMIN) { b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks, zoneName, LOBJ_PKGADMIN, ERR_ZONES_ULK_THIS_PACKAGE); if (!b) { errors = B_TRUE; } } /* unlock zone administration */ if (a_lflags & ZLOCKS_ZONE_ADMIN) { b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks, zoneName, LOBJ_ZONEADMIN, ERR_ZONES_ULK_THIS_ZONES); if (!b) { errors = B_TRUE; } } (void) free(zoneName); return (!errors); } /* * Name: z_unlock_zones * Description: unlock specified zones * Arguments: a_zlst - zoneList_t object describing zones to unlock * a_lflags - [RO, *RO] - (ZLOCKS_T) * Flags indicating which locks to release * Returns: boolean_t * == B_TRUE - success, zones unlocked * == B_FALSE - failure, zones not unlocked */ boolean_t z_unlock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags) { boolean_t b; boolean_t errors = B_FALSE; int i; /* entry assertions */ assert(a_lflags != ZLOCKS_NONE); /* entry debugging info */ _z_echoDebug(DBG_ZONES_ULK_ZONES, a_lflags); /* if zones are not implemented, return TRUE */ if (z_zones_are_implemented() == B_FALSE) { _z_echoDebug(DBG_ZONES_ULK_ZONES_UNIMP); return (B_TRUE); } /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { _z_echoDebug(DBG_ZONES_ULK_ZONES_NOZONES); /* unlock this zone before returning */ return (z_unlock_this_zone(a_lflags)); } /* zones exist */ _z_echoDebug(DBG_ZONES_ULK_ZONES_EXIST); /* * unlock each listed zone that is currently running */ for (i = 0; (a_zlst[i]._zlName != (char *)NULL); i++) { /* ignore zone if not locked */ if (!(a_zlst[i]._zlStatus & ZST_LOCKED)) { continue; } /* ignore zone if not running */ if (a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_RUNNING && a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_MOUNTED) { continue; } /* unlock this zone */ b = _z_unlock_zone(&a_zlst[i], a_lflags); if (b != B_TRUE) { errors = B_TRUE; } else { /* mark zone as unlocked */ a_zlst[i]._zlStatus &= ~ZST_LOCKED; } } /* unlock this zone */ if (z_unlock_this_zone(a_lflags) != B_TRUE) { errors = B_TRUE; } return (errors); } /* * Name: z_verify_zone_spec * Description: Verify list of zones on which actions will be performed. * Arguments: None. * Returns: 0 on success, -1 on error. * NOTES: Will call _z_program_error if there are zones on the specified * list that don't exist on the system. Requires that * z_set_zone_root is called first (if it is called at all). */ int z_verify_zone_spec(void) { FILE *zoneIndexFP; boolean_t errors; char zoneIndexPath[MAXPATHLEN]; struct zoneent *ze; zone_spec_t *zent; if (!z_zones_are_implemented()) { _z_program_error(ERR_ZONES_NOT_IMPLEMENTED); return (-1); } zoneIndexFP = setzoneent(); if (zoneIndexFP == NULL) { _z_program_error(ERR_ZONEINDEX_OPEN, zoneIndexPath, strerror(errno)); return (-1); } while ((ze = getzoneent_private(zoneIndexFP)) != NULL) { for (zent = _z_global_data._zone_spec; zent != NULL; zent = zent->zl_next) { if (strcmp(zent->zl_name, ze->zone_name) == 0) { zent->zl_used = B_TRUE; break; } } free(ze); } endzoneent(zoneIndexFP); errors = B_FALSE; for (zent = _z_global_data._zone_spec; zent != NULL; zent = zent->zl_next) { if (!zent->zl_used) { _z_program_error(ERR_ZONE_NONEXISTENT, zent->zl_name); errors = B_TRUE; } } return (errors ? -1 : 0); } /* * Name: z_zlist_change_zone_state * Description: Change the current state of the specified zone * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to return the * a_newState - the state to put the specified zone in * Returns: boolean_t * == B_TRUE - the zone is in the new state * == B_FALSE - unable to transition the zone to the * specified state * NOTE: This changes the "current kernel" state of the specified * zone. For example, to boot the zone, change the state * to "ZONE_STATE_RUNNING". To halt the zone, change the * state to "ZONE_STATE_INSTALLED". */ boolean_t z_zlist_change_zone_state(zoneList_t a_zlst, int a_zoneIndex, zone_state_t a_newState) { int i; /* entry debugging info */ _z_echoDebug(DBG_ZONES_CHG_Z_STATE_ENTRY, a_zoneIndex, a_newState); /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return (B_FALSE); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (B_FALSE); } /* return success if the zone is already in this state */ if (a_zlst[i]._zlCurrKernelStatus == a_newState) { return (B_TRUE); } /* take action on new state to set zone to */ _z_echoDebug(DBG_ZONES_CHG_Z_STATE, a_zlst[i]._zlName, a_zlst[i]._zlCurrKernelStatus, a_newState); switch (a_newState) { case ZONE_STATE_RUNNING: case ZONE_STATE_MOUNTED: /* these states mean "boot the zone" */ return (_z_make_zone_running(&a_zlst[i])); case ZONE_STATE_DOWN: case ZONE_STATE_INSTALLED: /* these states mean "halt the zone" */ return (_z_make_zone_down(&a_zlst[i])); case ZONE_STATE_READY: return (_z_make_zone_ready(&a_zlst[i])); case ZONE_STATE_CONFIGURED: case ZONE_STATE_INCOMPLETE: case ZONE_STATE_SHUTTING_DOWN: default: /* do not know how to change zone to this state */ return (B_FALSE); } } /* * Name: z_is_zone_branded * Description: Determine whether zone has a non-native brand * Arguments: a_zoneName - name of the zone to check for branding * Returns: boolean_t * == B_TRUE - zone has a non-native brand * == B_FALSE - zone is native */ boolean_t z_is_zone_branded(char *zoneName) { char brandname[MAXNAMELEN]; int err; /* if zones are not implemented, return FALSE */ if (!z_zones_are_implemented()) { return (B_FALSE); } /* if brands are not implemented, return FALSE */ if (!z_brands_are_implemented()) { return (B_FALSE); } err = zone_get_brand(zoneName, brandname, sizeof (brandname)); if (err != Z_OK) { _z_program_error(ERR_BRAND_GETBRAND, zonecfg_strerror(err)); return (B_FALSE); } /* * Both "native" and "cluster" are native brands * that use the standard facilities in the areas * of packaging/installation/update. */ if (streq(brandname, NATIVE_BRAND_NAME) || streq(brandname, CLUSTER_BRAND_NAME)) { return (B_FALSE); } else { return (B_TRUE); } } /* * Name: z_is_zone_brand_in_list * Description: Determine whether zone's brand has a match in the list * brands passed in. * Arguments: zoneName - name of the zone to check for branding * list - list of brands to check the zone against * Returns: boolean_t * == B_TRUE - zone has a matching brand * == B_FALSE - zone brand is not in list */ boolean_t z_is_zone_brand_in_list(char *zoneName, zoneBrandList_t *list) { char brandname[MAXNAMELEN]; int err; zoneBrandList_t *sp; if (zoneName == NULL || list == NULL) return (B_FALSE); /* if zones are not implemented, return FALSE */ if (!z_zones_are_implemented()) { return (B_FALSE); } /* if brands are not implemented, return FALSE */ if (!z_brands_are_implemented()) { return (B_FALSE); } err = zone_get_brand(zoneName, brandname, sizeof (brandname)); if (err != Z_OK) { _z_program_error(ERR_BRAND_GETBRAND, zonecfg_strerror(err)); return (B_FALSE); } for (sp = list; sp != NULL; sp = sp->next) { if (sp->string_ptr != NULL && strcmp(sp->string_ptr, brandname) == 0) { return (B_TRUE); } } return (B_FALSE); } /* * Name: z_zlist_get_current_state * Description: Determine the current kernel state of the specified zone * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to return * Returns: zone_state_t * The current state of the specified zone is returned */ zone_state_t z_zlist_get_current_state(zoneList_t a_zlst, int a_zoneIndex) { int i; /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return (ZONE_STATE_INCOMPLETE); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (ZONE_STATE_INCOMPLETE); } /* return selected zone's current kernel state */ _z_echoDebug(DBG_ZONES_GET_ZONE_STATE, a_zlst[i]._zlName ? a_zlst[i]._zlName : "", a_zlst[i]._zlCurrKernelStatus); return (a_zlst[i]._zlCurrKernelStatus); } /* * Name: z_zlist_get_original_state * Description: Return the original kernal state of the specified zone * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to return the * Returns: zone_state_t * The original state of the specified zone is returned. * This is the state of the zone when the zoneList_t * object was first generated. */ zone_state_t z_zlist_get_original_state(zoneList_t a_zlst, int a_zoneIndex) { int i; /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return (ZONE_STATE_INCOMPLETE); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (ZONE_STATE_INCOMPLETE); } /* return selected zone's original kernel state */ return (a_zlst[i]._zlOrigKernelStatus); } /* * Name: z_zlist_get_scratch * Description: Determine name of scratch zone * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to use * Return: char * * == NULL - zone name could not be determined * != NULL - pointer to string representing scratch zone * NOTE: Any name returned is placed in static storage that must * NEVER be free()ed by the caller. */ char * z_zlist_get_scratch(zoneList_t a_zlst, int a_zoneIndex) { int i; /* ignore empty list */ if (a_zlst == NULL) return (NULL); /* find the specified zone in the list */ for (i = 0; i != a_zoneIndex; i++) { if (a_zlst[i]._zlName == NULL) return (NULL); } /* return selected zone's scratch name */ return (a_zlst[i]._zlScratchName == NULL ? a_zlst[i]._zlName : a_zlst[i]._zlScratchName); } /* * Name: z_zlist_get_zonename * Description: Determine name of specified zone * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to return the * Return: char * * == NULL - zone name could not be determined * != NULL - pointer to string representing zone name * NOTE: Any zoneList_t returned is placed in static storage that must * NEVER be free()ed by the caller. */ char * z_zlist_get_zonename(zoneList_t a_zlst, int a_zoneIndex) { int i; /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return ((char *)NULL); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (NULL); } /* return selected zone's name */ return (a_zlst[i]._zlName); } /* * Name: z_zlist_get_zonepath * Description: Determine zonepath of specified zone * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to return * Return: char * * == NULL - zonepath could not be determined * != NULL - pointer to string representing zonepath * NOTE: Any zoneList_t returned is placed in static storage that must * NEVER be free()ed by the caller. */ char * z_zlist_get_zonepath(zoneList_t a_zlst, int a_zoneIndex) { int i; /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return ((char *)NULL); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (NULL); } /* return selected zone's zonepath */ return (a_zlst[i]._zlPath); } boolean_t z_zlist_is_zone_runnable(zoneList_t a_zlst, int a_zoneIndex) { int i; /* if zones are not implemented, return error */ if (z_zones_are_implemented() == B_FALSE) { return (B_FALSE); } /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return (B_FALSE); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (B_FALSE); } /* choose based on current state */ switch (a_zlst[i]._zlCurrKernelStatus) { case ZONE_STATE_RUNNING: case ZONE_STATE_MOUNTED: /* already running */ return (B_TRUE); case ZONE_STATE_INSTALLED: case ZONE_STATE_DOWN: case ZONE_STATE_READY: case ZONE_STATE_SHUTTING_DOWN: /* return false if the zone cannot be booted */ if (a_zlst[i]._zlStatus & ZST_NOT_BOOTABLE) { return (B_FALSE); } return (B_TRUE); case ZONE_STATE_CONFIGURED: case ZONE_STATE_INCOMPLETE: default: /* cannot transition (boot) these states */ return (B_FALSE); } } /* * Name: z_zlist_restore_zone_state * Description: Return the zone to the state it was originally in * Arguments: a_zlst - handle to zoneList_t object describing all zones * a_zoneIndex - index into a_zlst of the zone to return the * Returns: boolean_t * == B_TRUE - the zone's state has been restored * == B_FALSE - unable to transition the zone to its * original state */ boolean_t z_zlist_restore_zone_state(zoneList_t a_zlst, int a_zoneIndex) { int i; /* ignore empty list */ if (a_zlst == (zoneList_t)NULL) { return (B_FALSE); } /* find the specified zone in the list */ for (i = 0; (i != a_zoneIndex) && (a_zlst[i]._zlName != (char *)NULL); i++) ; /* return error if the specified zone does not exist */ if (a_zlst[i]._zlName == (char *)NULL) { return (B_FALSE); } /* transition the zone back to its original state */ return (z_zlist_change_zone_state(a_zlst, a_zoneIndex, a_zlst[i]._zlOrigKernelStatus)); } /* * Name: z_zone_exec * Description: Execute a Unix command in a specified zone and return results * Arguments: a_zoneName - pointer to string representing the name of the zone * to execute the specified command in * a_path - pointer to string representing the full path *in the * non-global zone named by a_zoneName* of the Unix command * to be executed * a_argv[] - Pointer to array of character strings representing * the arguments to be passed to the Unix command. The list * must be termianted with an element that is (char *)NULL * NOTE: a_argv[0] is the "command name" passed to the command * a_stdoutPath - Pointer to string representing the path to a file * into which all output to "stdout" from the Unix command * is placed. * == (char *)NULL - leave stdout open and pass through * == "/dev/null" - discard stdout output * a_strerrPath - Pointer to string representing the path to a file * into which all output to "stderr" from the Unix command * is placed. * == (char *)NULL - leave stderr open and pass through * == "/dev/null" - discard stderr output * a_fds - Pointer to array of integers representing file * descriptors to remain open during the call - all * file descriptors above STDERR_FILENO not in this * list will be closed. * Returns: int * The return (exit) code from the specified Unix command * Special return codes: * -1 : failure to exec process * -2 : could not create contract for greenline * -3 : fork() failed * -4 : could not open stdout capture file * -5 : error from 'waitpid' other than EINTR * -6 : zones are not supported * NOTE: All file descriptores other than 0, 1 and 2 are closed except * for those file descriptors listed in the a_fds array. */ int z_zone_exec(const char *a_zoneName, const char *a_path, char *a_argv[], char *a_stdoutPath, char *a_stderrPath, int *a_fds) { int final_status; int lerrno; int status; int tmpl_fd; pid_t child_pid; pid_t result_pid; struct sigaction nact; struct sigaction oact; void (*funcSighup)(); void (*funcSigint)(); /* if zones are not implemented, return TRUE */ if (z_zones_are_implemented() == B_FALSE) { return (-6); /* -6 : zones are not supported */ } if ((tmpl_fd = _zexec_init_template()) == -1) { _z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno)); return (-2); /* -2 : could not create greenline contract */ } /* * hold SIGINT/SIGHUP signals and reset signal received counter; * after the fork1() the parent and child need to setup their respective * interrupt handling and release the hold on the signals */ (void) sighold(SIGINT); (void) sighold(SIGHUP); _z_global_data._z_SigReceived = 0; /* no signals received */ /* * fork off a new process to execute command in; * fork1() is used instead of vfork() so the child process can * perform operations that would modify the parent process if * vfork() were used */ child_pid = fork1(); if (child_pid < 0) { /* * ************************************************************* * fork failed! * ************************************************************* */ (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); _z_program_error(ERR_FORK, strerror(errno)); /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); return (-3); /* -3 : fork() failed */ } if (child_pid == 0) { int i; /* * ************************************************************* * This is the forked (child) process * ************************************************************* */ (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); /* reset any signals to default */ for (i = 0; i < NSIG; i++) { (void) sigset(i, SIG_DFL); } /* * close all file descriptors not in the a_fds list */ (void) fdwalk(&_z_close_file_descriptors, (void *)a_fds); /* * if a file for stdout is present, open the file and use the * file to capture stdout from the _zexec process */ if (a_stdoutPath != (char *)NULL) { int stdoutfd; stdoutfd = open(a_stdoutPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (stdoutfd < 0) { _z_program_error(ERR_CAPTURE_FILE, a_stdoutPath, strerror(errno)); return (-4); } (void) dup2(stdoutfd, STDOUT_FILENO); (void) close(stdoutfd); } /* * if a file for stderr is present, open the file and use the * file to capture stderr from the _zexec process */ if (a_stderrPath != (char *)NULL) { int stderrfd; stderrfd = open(a_stderrPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (stderrfd < 0) { _z_program_error(ERR_CAPTURE_FILE, a_stderrPath, strerror(errno)); return (-4); } (void) dup2(stderrfd, STDERR_FILENO); (void) close(stderrfd); } /* release all held signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); /* execute command in the specified non-global zone */ _exit(_zexec(a_zoneName, a_path, a_argv)); } /* * ********************************************************************* * This is the forking (parent) process * ********************************************************************* */ /* register child process i.d. so signal handlers can pass signal on */ _z_global_data._z_ChildProcessId = child_pid; /* * setup signal handlers for SIGINT and SIGHUP and release hold */ /* hook SIGINT to _z_sig_trap() */ nact.sa_handler = _z_sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGINT, &nact, &oact) < 0) { funcSigint = SIG_DFL; } else { funcSigint = oact.sa_handler; } /* hook SIGHUP to _z_sig_trap() */ nact.sa_handler = _z_sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGHUP, &nact, &oact) < 0) { funcSighup = SIG_DFL; } else { funcSighup = oact.sa_handler; } /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); /* * wait for the process to exit, reap child exit status */ for (;;) { result_pid = waitpid(child_pid, &status, 0L); lerrno = (result_pid == -1 ? errno : 0); /* break loop if child process status reaped */ if (result_pid != -1) { break; } /* break loop if not interrupted out of waitpid */ if (errno != EINTR) { break; } } /* reset child process i.d. so signal handlers do not pass signals on */ _z_global_data._z_ChildProcessId = -1; /* * If the child process terminated due to a call to exit(), then * set results equal to the 8-bit exit status of the child process; * otherwise, set the exit status to "-1" indicating that the child * exited via a signal. */ if (WIFEXITED(status)) { final_status = WEXITSTATUS(status); if ((_z_global_data._z_SigReceived != 0) && (final_status == 0)) { final_status = 1; } } else { final_status = -1; /* -1 : failure to exec process */ } /* determine proper exit code */ if (result_pid == -1) { final_status = -5; /* -5 : error from waitpid not EINTR */ } else if (_z_global_data._z_SigReceived != 0) { final_status = -7; /* -7 : interrupt received */ } /* * reset signal handlers */ /* reset SIGINT */ nact.sa_handler = funcSigint; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL); /* reset SIGHUP */ nact.sa_handler = funcSighup; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL); /* * if signal received during command execution, interrupt * this process now. */ if (_z_global_data._z_SigReceived != 0) { (void) kill(getpid(), SIGINT); } /* set errno and return */ errno = lerrno; return (final_status); } /* * Name: z_zones_are_implemented * Description: Determine if any zone operations can be performed * Arguments: void * Returns: boolean_t * == B_TRUE - zone operations are available * == B_FALSE - no zone operations can be done */ boolean_t z_zones_are_implemented(void) { static boolean_t _zonesImplementedDetermined = B_FALSE; static boolean_t _zonesAreImplemented = B_FALSE; /* if availability has not been determined, cache it now */ if (!_zonesImplementedDetermined) { _zonesImplementedDetermined = B_TRUE; _zonesAreImplemented = _z_zones_are_implemented(); if (!_zonesAreImplemented) { _z_echoDebug(DBG_ZONES_NOT_IMPLEMENTED); } else { _z_echoDebug(DBG_ZONES_ARE_IMPLEMENTED); } } return (_zonesAreImplemented); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Module: zones_args.c * Group: libinstzones * Description: Private functions used by zones library functions to manipulate * argument lists * * Public Methods: * * _z_add_arg - add new argument to argument array for use in exec() calls * _z_free_args - free all storage contained in an argument array previously * _z_get_argc - return (int) argc count from argument array * _z_get_argv - return (char **)argv pointer from argument array * _z_new_args - create a new argument array for use in exec() calls */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ /* * Library Function Prototypes */ /* * Local Function Prototypes */ /* * Global internal (private) declarations */ /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: _z_add_arg * Description: add new argument to argument array for use in exec() calls * Arguments: a_args - [RO, *RW] - (argArray_t *) * Pointer to argument array (previously allocated via * a call to _z_new_args) to add the argument to * a_format - [RO, *RO] - (char *) * Pointer to "printf(3C)" style format argument * ... - [RO, *RO] - (varies) * Arguments as appropriate for format argument specified * Returns: boolean_t * B_TRUE - success * B_FALSE - failure * Examples: * - to add an argument that specifies a file descriptor: * int fd; * _z_add_arg(aa, "/proc/self/fd/%d", fd); * - to add a flag or other known text: * _z_add_arg(aa, "-s") * - to add random text: * char *random_text; * _z_add_arg(aa, "%s", random_text); */ /*PRINTFLIKE2*/ boolean_t _z_add_arg(argArray_t *a_args, char *a_format, ...) { char *rstr = NULL; char bfr[MAX_CANON]; size_t vres = 0; va_list ap; /* entry assertions */ assert(a_args != NULL); assert(a_format != NULL); assert(*a_format != '\0'); /* * double argument array if array is full */ if (a_args->_aaNumArgs >= a_args->_aaMaxArgs) { int newMax; char **newArgs; newMax = a_args->_aaMaxArgs * 2; newArgs = (char **)_z_realloc(a_args->_aaArgs, (newMax+1) * sizeof (char *)); a_args->_aaArgs = newArgs; a_args->_aaMaxArgs = newMax; } /* * determine size of argument to add to list */ va_start(ap, a_format); vres = vsnprintf(bfr, sizeof (bfr), a_format, ap); va_end(ap); /* * use the expanded argument if it will fit in the built in buffer, * otherwise, allocate space to hold the argument */ if (vres < sizeof (bfr)) { /* duplicate text already generated in buffer */ rstr = _z_strdup(bfr); } else { /* allocate new space for argument to add */ rstr = (char *)_z_malloc(vres+2); /* generate argument to add */ va_start(ap, a_format); vres = vsnprintf(rstr, vres+1, a_format, ap); va_end(ap); } /* add argument to the end of the argument array */ a_args->_aaArgs[a_args->_aaNumArgs++] = rstr; a_args->_aaArgs[a_args->_aaNumArgs] = NULL; /* successful - return */ return (B_TRUE); } /* * Name: _z_free_args * Description: free all storage contained in an argument array previously * allocated by a call to _z_new_args * Arguments: a_args - [RO, *RW] - (argArray_t *) * Pointer to argument array (previously allocated via * a call to _z_new_args) to free * Returns: void * NOTE: preserves errno (usually called right after e_execCmd*()) */ void _z_free_args(argArray_t *a_args) { int i; int lerrno = errno; /* entry assertions */ assert(a_args != NULL); assert(a_args->_aaArgs != NULL); /* free all arguments in the argument array */ for (i = (a_args->_aaNumArgs-1); i >= 0; i--) { assert(a_args->_aaArgs[i] != NULL); (void) free(a_args->_aaArgs[i]); } /* free argument array */ (void) free(a_args->_aaArgs); /* free argument array structure */ (void) free(a_args); /* restore errno */ errno = lerrno; } /* * Name: _z_get_argc * Description: return (int) argc count from argument array * Arguments: a_args - [RO, *RW] - (argArray_t *) * Pointer to argument array (previously allocated via * a call to _z_new_args) to return argc count for * Returns: int * Count of the number of arguments in the argument array * suitable for use in an exec*() call */ int _z_get_argc(argArray_t *a_args) { return (a_args->_aaNumArgs); } /* * Name: _z_get_argv * Description: return (char **)argv pointer from argument array * Arguments: a_args - [RO, *RW] - (argArray_t *) * Pointer to argument array (previously allocated via * a call to _z_new_args) to return argv pointer for * Returns: char ** * Pointer to (char **)argv pointer suitable for use * in an exec*() call * NOTE: the actual character array is always terminated with a NULL */ char ** _z_get_argv(argArray_t *a_args) { return (a_args->_aaArgs); } /* * Name: _z_new_args * Description: create a new argument array for use in exec() calls * Arguments: initialCount - [RO, *RO] - (int) * Initial number of elements to populate the * argument array with - use best guess * Returns: argArray_t * * Pointer to argument array that can be used in other * functions that accept it as an argument * == (argArray_t *)NULL - error * NOTE: you must call _z_free_args() when the returned argument array is * no longer needed so that all storage used can be freed up. */ argArray_t * _z_new_args(int initialCount) { argArray_t *aa; /* entry assertions */ assert(initialCount >= 0); /* if no guess on size, then assume 1 */ if (initialCount == 0) { initialCount = 1; } /* allocate new argument array structure */ aa = (argArray_t *)_z_calloc(sizeof (argArray_t)); /* allocate initial argument array */ aa->_aaArgs = (char **)_z_calloc((initialCount+1) * sizeof (char *)); /* initialize argument indexes */ aa->_aaNumArgs = 0; aa->_aaMaxArgs = initialCount; /* successful - return pointer to argument array created */ return (aa); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Module: zones_exec.c * Group: libinstzones * Description: Provide "zones" execution interface for install * consolidation code * * Public Methods: * * z_ExecCmdArray - Execute a Unix command and return results and status * _zexec - run a command with arguments on a specified zone * _zexec_init_template - used by _zexec to establish contracts * _z_zone_exec - Execute a Unix command in a specified zone and return results * z_ExecCmdList - Execute a Unix command and return results and status */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ /* * Library Function Prototypes */ /* * Local Function Prototypes */ /* * global internal (private) declarations */ /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: z_ExecCmdArray * Synopsis: Execute Unix command and return results * Description: Execute a Unix command and return results and status * Arguments: * r_status - [RO, *RW] - (int *) * Return (exit) status from Unix command: * == -1 : child terminated with a signal * != -1 : lower 8-bit value child passed to exit() * r_results - [RO, *RW] - (char **) * Any output generated by the Unix command to stdout * and to stderr * == (char *)NULL if no output generated * a_inputFile - [RO, *RO] - (char *) * Pointer to character string representing file to be * used as "standard input" for the command. * == (char *)NULL to use "/dev/null" as standard input * a_cmd - [RO, *RO] - (char *) * Pointer to character string representing the full path * of the Unix command to execute * char **a_args - [RO, *RO] - (char **) * List of character strings representing the arguments * to be passed to the Unix command. The list must be * terminated with an element that is (char *)NULL * Returns: int * == 0 - Command executed * Look at r_status for results of Unix command * != 0 - problems executing command * r_status and r_results have no meaning; * r_status will be -1 * r_results will be NULL * NOTE: Any results returned is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the results are no longer needed. * NOTE: If 0 is returned, 'r_status' must be queried to * determine the results of the Unix command. * NOTE: The system "errno" value from immediately after waitpid() call * is preserved for the calling method to use to determine * the system reason why the operation failed. */ int z_ExecCmdArray(int *r_status, char **r_results, char *a_inputFile, char *a_cmd, char **a_args) { char *buffer; int bufferIndex; int bufferSize; int ipipe[2] = {0, 0}; int lerrno; int status; int stdinfile = -1; pid_t pid; pid_t resultPid; /* entry assertions */ assert(r_status != NULL); assert(a_cmd != NULL); assert(*a_cmd != '\0'); assert(a_args != NULL); /* reset return results buffer pointer */ if (r_results != (char **)NULL) { *r_results = (char *)NULL; } *r_status = -1; /* * See if command exists */ if (access(a_cmd, F_OK|X_OK) != 0) { return (-1); } /* * See if input file exists */ if (a_inputFile != (char *)NULL) { stdinfile = open(a_inputFile, O_RDONLY); } else { stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */ } if (stdinfile < 0) { return (-1); } /* * Create a pipe to be used to capture the command output */ if (pipe(ipipe) != 0) { (void) close(stdinfile); return (-1); } bufferSize = PIPE_BUFFER_INCREMENT; bufferIndex = 0; buffer = calloc(1, bufferSize); if (buffer == (char *)NULL) { (void) close(stdinfile); return (-1); } /* flush standard i/o before creating new process */ (void) fflush(stderr); (void) fflush(stdout); /* * create new process to execute command in; * vfork() is being used to avoid duplicating the parents * memory space - this means that the child process may * not modify any of the parents memory including the * standard i/o descriptors - all the child can do is * adjust interrupts and open files as a prelude to a * call to exec(). */ pid = vfork(); if (pid == 0) { /* * This is the forked (child) process ====================== */ int i; /* reset any signals to default */ for (i = 0; i < NSIG; i++) { (void) sigset(i, SIG_DFL); } /* assign stdin, stdout, stderr as appropriate */ (void) dup2(stdinfile, STDIN_FILENO); (void) close(ipipe[0]); /* close out pipe reader side */ (void) dup2(ipipe[1], STDOUT_FILENO); (void) dup2(ipipe[1], STDERR_FILENO); /* Close all open files except standard i/o */ closefrom(3); /* execute target executable */ (void) execvp(a_cmd, a_args); perror(a_cmd); /* Emit error msg - ends up in callers buffer */ _exit(0x00FE); } else if (pid == -1) { _z_program_error(ERR_FORK, strerror(errno)); *r_status = -1; return (-1); } /* * This is the forking (parent) process ==================== */ (void) close(stdinfile); (void) close(ipipe[1]); /* Close write side of pipe */ /* * Spin reading data from the child into the buffer - when the read eofs * the child has exited */ for (;;) { ssize_t bytesRead; /* read as much child data as there is available buffer space */ bytesRead = read(ipipe[0], buffer + bufferIndex, bufferSize - bufferIndex); /* break out of read loop if end-of-file encountered */ if (bytesRead == 0) { break; } /* if error, continue if recoverable, else break out of loop */ if (bytesRead == -1) { /* try again: EAGAIN - insufficient resources */ if (errno == EAGAIN) { continue; } /* try again: EINTR - interrupted system call */ if (errno == EINTR) { continue; } /* break out of loop - error not recoverable */ break; } /* at least 1 byte read: expand buffer if at end */ bufferIndex += bytesRead; if (bufferIndex >= bufferSize) { buffer = realloc(buffer, bufferSize += PIPE_BUFFER_INCREMENT); (void) memset(buffer + bufferIndex, 0, bufferSize - bufferIndex); } } (void) close(ipipe[0]); /* Close read side of pipe */ /* Get subprocess exit status */ for (;;) { resultPid = waitpid(pid, &status, 0L); lerrno = (resultPid == -1 ? errno : 0); /* break loop if child process status reaped */ if (resultPid != -1) { break; } /* break loop if not interrupted out of waitpid */ if (errno != EINTR) { break; } } /* * If the child process terminated due to a call to exit(), then * set results equal to the 8-bit exit status of the child process; * otherwise, set the exit status to "-1" indicating that the child * exited via a signal. */ *r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1; /* return appropriate output */ if (!*buffer) { /* No contents in output buffer - discard */ free(buffer); } else if (r_results == (char **)NULL) { /* Not requested to return results - discard */ free(buffer); } else { /* have output and request to return: pass to calling method */ *r_results = buffer; } errno = lerrno; return (resultPid == -1 ? -1 : 0); } /* * Name: _zexec * Description: run a command with arguments on a specified zone * Arguments: a_zoneName - pointer to string representing the name of the zone * to execute the specified command in * a_path - pointer to string representing the full path *in the * non-global zone named by a_zoneName* of the Unix command * to be executed * a_argv[] - Pointer to array of character strings representing * the arguments to be passed to the Unix command. The list * must be termianted with an element that is (char *)NULL * NOTE: a_argv[0] is the "command name" passed to the command * Returns: int * This function must be treated like a call to exec() * If the exec() is successful, the thread of control is * NOT returned, and the process will exit when completed. * If this function returns, it means the exec() could not * be done, or another fatal error occurred. */ int _zexec(const char *a_zoneName, const char *a_path, char *a_argv[]) { zoneid_t zoneid; zone_state_t st; char **new_env = { NULL }; priv_set_t *privset; /* entry assertions */ assert(a_zoneName != NULL); assert(*a_zoneName != '\0'); assert(a_path != NULL); assert(*a_path != '\0'); /* establish locale settings */ (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* can only be invoked from within the global zone */ if (getzoneid() != GLOBAL_ZONEID) { _z_program_error(ERR_ZEXEC_NOT_IN_GZ, a_zoneName); return (-1); } if (strcmp(a_zoneName, GLOBAL_ZONENAME) == 0) { _z_program_error(ERR_ZEXEC_GZUSED, a_zoneName); return (-1); } /* get the state of the specified zone */ if (zone_get_state((char *)a_zoneName, &st) != Z_OK) { _z_program_error(ERR_ZEXEC_BADZONE, a_zoneName); return (-1); } if (st < ZONE_STATE_INSTALLED) { _z_program_error(ERR_ZEXEC_BADSTATE, a_zoneName, zone_state_str(st)); return (-1); } if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) { _z_program_error(ERR_ZEXEC_NOTRUNNING, a_zoneName, zone_state_str(st)); return (-1); } /* * In both console and non-console cases, we require all privs. * In the console case, because we may need to startup zoneadmd. * In the non-console case in order to do zone_enter(2), zonept() * and other tasks. * * Future work: this solution is temporary. Ultimately, we need to * move to a flexible system which allows the global admin to * designate that a particular user can zlogin (and probably zlogin * -C) to a particular zone. This all-root business we have now is * quite sketchy. */ if ((privset = priv_allocset()) == NULL) { _z_program_error(ERR_ZEXEC_PRIV_ALLOCSET, a_zoneName, strerror(errno)); return (-1); } if (getppriv(PRIV_EFFECTIVE, privset) != 0) { _z_program_error(ERR_ZEXEC_GETPPRIV, a_zoneName, strerror(errno)); priv_freeset(privset); return (-1); } if (priv_isfullset(privset) == B_FALSE) { _z_program_error(ERR_ZEXEC_PRIVS, a_zoneName); priv_freeset(privset); return (-1); } priv_freeset(privset); if ((zoneid = getzoneidbyname(a_zoneName)) == -1) { _z_program_error(ERR_ZEXEC_NOZONEID, a_zoneName, strerror(errno)); return (-1); } if ((new_env = _zexec_prep_env()) == NULL) { _z_program_error(ERR_ZEXEC_ASSEMBLE, a_zoneName); return (-1); } /* * In case any of stdin, stdout or stderr are streams, * anchor them to prevent malicious I_POPs. * * Future work: use pipes to entirely eliminate FD leakage * into the zone. */ (void) ioctl(STDIN_FILENO, I_ANCHOR); (void) ioctl(STDOUT_FILENO, I_ANCHOR); (void) ioctl(STDERR_FILENO, I_ANCHOR); if (zone_enter(zoneid) == -1) { int lerrno = errno; _z_program_error(ERR_ZEXEC_ZONEENTER, a_zoneName, strerror(errno)); if (lerrno == EFAULT) { _z_program_error(ERR_ZEXEC_EFAULT, a_zoneName); } free(new_env); return (-1); } (void) execve(a_path, &a_argv[0], new_env); _z_program_error(ERR_ZEXEC_EXECFAILURE, a_zoneName, strerror(errno)); return (-1); } /* * Name: _zexec_init_template * Description: used by _zexec to establish contracts */ int _zexec_init_template(void) { int fd; int err = 0; fd = open64(CTFS_ROOT "/process/template", O_RDWR); if (fd == -1) { return (-1); } /* * zlogin doesn't do anything with the contract. * Deliver no events, don't inherit, and allow it to be orphaned. */ err |= ct_tmpl_set_critical(fd, 0); err |= ct_tmpl_set_informative(fd, 0); err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); if (err || ct_tmpl_activate(fd)) { (void) close(fd); return (-1); } return (fd); } /* * Helper routine for _zexec_prep_env below. */ char * _zexec_add_env(char *name, char *value) { size_t sz = strlen(name) + strlen(value) + 1; char *str; if ((str = malloc(sz)) == NULL) return (NULL); (void) snprintf(str, sz, "%s%s", name, value); return (str); } /* * Prepare envp array for exec'd process. */ char ** _zexec_prep_env() { int e = 0, size = 1; char **new_env, *estr; char *term = getenv("TERM"); size++; /* for $PATH */ if (term != NULL) size++; /* * In failsafe mode we set $HOME */ size++; /* * In failsafe mode we set $SHELL, since login won't be around to do it. */ size++; if ((new_env = malloc(sizeof (char *) * size)) == NULL) return (NULL); if ((estr = _zexec_add_env("PATH=", ZONE_DEF_PATH)) == NULL) { free(new_env); return (NULL); } new_env[e++] = estr; if (term != NULL) { if ((estr = _zexec_add_env("TERM=", term)) == NULL) { free(new_env); return (NULL); } new_env[e++] = estr; } if ((estr = _zexec_add_env("HOME=", "/")) == NULL) { free(new_env); return (NULL); } new_env[e++] = estr; if ((estr = _zexec_add_env("SHELL=", ZONE_FAILSAFESHELL)) == NULL) { free(new_env); return (NULL); } new_env[e++] = estr; new_env[e++] = NULL; return (new_env); } /* * Name: _z_zone_exec * Description: Execute a Unix command in a specified zone and return results * Arguments: * r_status - [RO, *RW] - (int *) * Return (exit) status from Unix command: * == -1 : child terminated with a signal * != -1 : lower 8-bit value child passed to exit() * r_results - [RO, *RW] - (char **) * Any output generated by the Unix command to stdout * and to stderr * == (char *)NULL if no output generated * a_inputFile - [RO, *RO] - (char *) * Pointer to character string representing file to be * used as "standard input" for the command. * == (char *)NULL to use "/dev/null" as standard input * a_path - [RO, *RO] - (char *) * Pointer to character string representing the full path * *in the non-global zone named by a_zoneName*of the Unix * command to be executed * char **a_args - [RO, *RO] - (char **) * List of character strings representing the arguments * to be passed to the Unix command. * NOTE: The list must be terminated with an element that * ----- is (char *)NULL * NOTE: a_argv[0] is the "command name" passed to the * ----- command executed in the specified non-global zone * a_zoneName - pointer to string representing the name of the zone * to execute the specified command in * a_fds - Pointer to array of integers representing file * descriptors to remain open during the call - all * file descriptors above STDERR_FILENO not in this * list will be closed. * Returns: int * == 0 - Command executed * Look at r_status for results of Unix command * != 0 - problems executing command * r_status and r_results have no meaning; * r_status will be -1 * r_results will be NULL * The return (exit) code from the specified Unix command * Special return codes: * -1 : failure to exec process * -2 : could not create contract for greenline * -3 : fork() failed * -4 : could not open stdin source file * -5 : error from 'waitpid' other than EINTR * -6 : zones are not supported * -7 : interrupt received * NOTE: All file descriptores other than 0, 1 and 2 are closed except * for those file descriptors listed in the a_fds array. */ int _z_zone_exec(int *r_status, char **r_results, char *a_inputFile, char *a_path, char *a_argv[], const char *a_zoneName, int *a_fds) { struct sigaction nact; struct sigaction oact; char *buffer; char *thisZoneName; int bufferIndex; int bufferSize; int exit_no; int ipipe[2] = {0, 0}; int lerrno; int n; int status; int stdinfile = -1; int tmpl_fd; pid_t child_pid; pid_t result_pid; void (*funcSighup)(); void (*funcSigint)(); /* entry assertions */ assert(a_path != (char *)NULL); assert(*a_path != '\0'); assert(a_argv != (char **)NULL); assert(a_argv[0] != (char *)NULL); assert(*a_argv[0] != '\0'); assert(a_zoneName != (char *)NULL); /* * if requested to execute in current zone name, directly execute */ thisZoneName = z_get_zonename(); status = (strcmp(a_zoneName, thisZoneName) == 0); /* entry debugging info */ _z_echoDebug(DBG_ZONE_EXEC_CMD_ENTER, a_path, a_zoneName, thisZoneName); (void) free(thisZoneName); for (n = 0; a_argv[n]; n++) { _z_echoDebug(DBG_ARG, n, a_argv[n]); } /* if this zone, just exec the command directly */ if (status != 0) { return (z_ExecCmdArray(r_status, r_results, a_inputFile, a_path, a_argv)); } /* reset return results buffer pointer */ if (r_results != (char **)NULL) { *r_results = (char *)NULL; } *r_status = -1; /* -1 : failure to exec process */ /* if zones are not implemented, return TRUE */ if (!z_zones_are_implemented()) { return (-6); /* -6 : zones are not supported */ } if ((tmpl_fd = _zexec_init_template()) == -1) { _z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno)); return (-2); /* -2 : cannot create greenline contract */ } /* * See if input file exists */ if (a_inputFile != (char *)NULL) { stdinfile = open(a_inputFile, O_RDONLY); } else { stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */ } if (stdinfile < 0) { return (-4); /* -4 : could not open stdin source file */ } /* * Create a pipe to be used to capture the command output */ if (pipe(ipipe) != 0) { (void) close(stdinfile); return (-1); } bufferSize = PIPE_BUFFER_INCREMENT; bufferIndex = 0; buffer = calloc(1, bufferSize); if (buffer == (char *)NULL) { (void) close(stdinfile); return (-1); } /* flush standard i/o before creating new process */ (void) fflush(stderr); (void) fflush(stdout); /* * hold SIGINT/SIGHUP signals and reset signal received counter; * after the fork1() the parent and child need to setup their respective * interrupt handling and release the hold on the signals */ (void) sighold(SIGINT); (void) sighold(SIGHUP); _z_global_data._z_SigReceived = 0; /* no signals received */ /* * fork off a new process to execute command in; * fork1() is used instead of vfork() so the child process can * perform operations that would modify the parent process if * vfork() were used */ child_pid = fork1(); if (child_pid < 0) { /* * ************************************************************* * fork failed! * ************************************************************* */ (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) free(buffer); _z_program_error(ERR_FORK, strerror(errno)); /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); return (-3); /* -3 : fork() failed */ } if (child_pid == 0) { int i; /* * ************************************************************* * This is the forked (child) process * ************************************************************* */ (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); /* reset any signals to default */ for (i = 0; i < NSIG; i++) { (void) sigset(i, SIG_DFL); } /* assign stdin, stdout, stderr as appropriate */ (void) dup2(stdinfile, STDIN_FILENO); (void) close(ipipe[0]); /* close out pipe reader side */ (void) dup2(ipipe[1], STDOUT_FILENO); (void) dup2(ipipe[1], STDERR_FILENO); /* * close all file descriptors not in the a_fds list */ (void) fdwalk(&_z_close_file_descriptors, (void *)a_fds); /* release all held signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); /* execute command in the specified non-global zone */ _exit(_zexec(a_zoneName, a_path, a_argv)); } /* * ********************************************************************* * This is the forking (parent) process * ********************************************************************* */ /* register child process i.d. so signal handlers can pass signal on */ _z_global_data._z_ChildProcessId = child_pid; /* * setup signal handlers for SIGINT and SIGHUP and release hold */ /* hook SIGINT to _z_sig_trap() */ nact.sa_handler = _z_sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGINT, &nact, &oact) < 0) { funcSigint = SIG_DFL; } else { funcSigint = oact.sa_handler; } /* hook SIGHUP to _z_sig_trap() */ nact.sa_handler = _z_sig_trap; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); if (sigaction(SIGHUP, &nact, &oact) < 0) { funcSighup = SIG_DFL; } else { funcSighup = oact.sa_handler; } /* release hold on signals */ (void) sigrelse(SIGHUP); (void) sigrelse(SIGINT); (void) ct_tmpl_clear(tmpl_fd); (void) close(tmpl_fd); (void) close(stdinfile); (void) close(ipipe[1]); /* Close write side of pipe */ /* * Spin reading data from the child into the buffer - when the read eofs * the child has exited */ for (;;) { ssize_t bytesRead; /* read as much child data as there is available buffer space */ bytesRead = read(ipipe[0], buffer + bufferIndex, bufferSize - bufferIndex); /* break out of read loop if end-of-file encountered */ if (bytesRead == 0) { break; } /* if error, continue if recoverable, else break out of loop */ if (bytesRead == -1) { /* try again: EAGAIN - insufficient resources */ if (errno == EAGAIN) { continue; } /* try again: EINTR - interrupted system call */ if (errno == EINTR) { continue; } /* break out of loop - error not recoverable */ break; } /* at least 1 byte read: expand buffer if at end */ bufferIndex += bytesRead; if (bufferIndex >= bufferSize) { buffer = realloc(buffer, bufferSize += PIPE_BUFFER_INCREMENT); (void) memset(buffer + bufferIndex, 0, bufferSize - bufferIndex); } } (void) close(ipipe[0]); /* Close read side of pipe */ /* * wait for the process to exit, reap child exit status */ for (;;) { result_pid = waitpid(child_pid, &status, 0L); lerrno = (result_pid == -1 ? errno : 0); /* break loop if child process status reaped */ if (result_pid != -1) { break; } /* break loop if not interrupted out of waitpid */ if (errno != EINTR) { break; } } /* reset child process i.d. so signal handlers do not pass signals on */ _z_global_data._z_ChildProcessId = -1; /* * If the child process terminated due to a call to exit(), then * set results equal to the 8-bit exit status of the child process; * otherwise, set the exit status to "-1" indicating that the child * exited via a signal. */ if (WIFEXITED(status)) { *r_status = WEXITSTATUS(status); if ((_z_global_data._z_SigReceived != 0) && (*r_status == 0)) { *r_status = 1; } } else { *r_status = -1; /* -1 : failure to exec process */ } /* determine proper exit code */ if (result_pid == -1) { exit_no = -5; /* -5 : error from 'waitpid' other than EINTR */ } else if (_z_global_data._z_SigReceived != 0) { exit_no = -7; /* -7 : interrupt received */ } else { exit_no = 0; } /* return appropriate output */ if (!*buffer) { /* No contents in output buffer - discard */ free(buffer); } else if (r_results == (char **)NULL) { /* Not requested to return results - discard */ free(buffer); } else { /* have output and request to return: pass to calling method */ *r_results = buffer; } /* * reset signal handlers */ /* reset SIGINT */ nact.sa_handler = funcSigint; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGINT, &nact, (struct sigaction *)NULL); /* reset SIGHUP */ nact.sa_handler = funcSighup; nact.sa_flags = SA_RESTART; (void) sigemptyset(&nact.sa_mask); (void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL); /* * if signal received during command execution, interrupt * this process now. */ if (_z_global_data._z_SigReceived != 0) { (void) kill(getpid(), SIGINT); } /* set errno and return */ errno = lerrno; return (exit_no); } /* * Name: z_ExecCmdList * Synopsis: Execute Unix command and return results * Description: Execute a Unix command and return results and status * Arguments: * r_status - [RO, *RW] - (int *) * Return (exit) status from Unix command * r_results - [RO, *RW] - (char **) * Any output generated by the Unix command to stdout * and to stderr * == (char *)NULL if no output generated * a_inputFile - [RO, *RO] - (char *) * Pointer to character string representing file to be * used as "standard input" for the command. * == (char *)NULL to use "/dev/null" as standard input * a_cmd - [RO, *RO] - (char *) * Pointer to character string representing the full path * of the Unix command to execute * ... - [RO] (?) * Zero or more arguments to the Unix command * The argument list must be ended with (void *)NULL * Returns: int * == 0 - Command executed * Look at r_status for results of Unix command * != 0 - problems executing command * r_status and r_results have no meaning * NOTE: Any results returned is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the results are no longer needed. * NOTE: If LU_SUCCESS is returned, 'r_status' must be queried to * determine the results of the Unix command. */ /*VARARGS*/ int z_ExecCmdList(int *r_status, char **r_results, char *a_inputFile, char *a_cmd, ...) { va_list ap; /* references variable argument list */ char *array[MAX_EXEC_CMD_ARGS+1]; int argno = 0; /* * Create argument array for exec system call */ bzero(array, sizeof (array)); va_start(ap, a_cmd); /* Begin variable argument processing */ for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) { array[argno] = va_arg(ap, char *); if (array[argno] == (char *)NULL) { break; } } va_end(ap); return (z_ExecCmdArray(r_status, r_results, a_inputFile, a_cmd, array)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2012 by Delphix. All rights reserved. */ /* * Module: zones_locks.c * Group: libinstzones * Description: Provide "zones" locking interfaces for install consolidation * code * * Public Methods: * * _z_acquire_lock - acquire a lock on an object on a zone * _z_adjust_lock_object_for_rootpath - Given a lock object and a root path, * if the root path is not * _z_lock_zone - Acquire specified locks on specified zone * _z_lock_zone_object - lock a single lock object in a specified zone * _z_release_lock - release a lock held on a zone * _z_unlock_zone - Released specified locks on specified zone * _z_unlock_zone_object - unlock a single lock object in a specified zone */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ /* * Library Function Prototypes */ /* * Local Function Prototypes */ boolean_t _z_adjust_lock_object_for_rootpath(char **r_result, char *a_lockObject); boolean_t _z_acquire_lock(char **r_lockKey, char *a_zoneName, char *a_lock, pid_t a_pid, boolean_t a_wait); boolean_t _z_lock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags); boolean_t _z_lock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject, pid_t a_pid, char *a_waitingMsg, char *a_busyMsg); boolean_t _z_release_lock(char *a_zoneName, char *a_lock, char *a_key, boolean_t a_wait); boolean_t _z_unlock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags); boolean_t _z_unlock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject, char *a_errMsg); /* * global internal (private) declarations */ /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: _z_acquire_lock * Description: acquire a lock on an object on a zone * Arguments: r_lockKey - [RW, *RW] - (char *) * Pointer to handle to string representing the lock key * associated with the lock object to be acquired - this * key is returned when the lock is acquired and must be * provided when releasing the lock * == (char *)NULL - lock not acquired * a_zoneName - [RO, *RO] - (char *) * Pointer to string representing the name of the zone to * acquire the specified lock on * a_lockObject - [RO, *RO] - (char *) * Pointer to string representing the lock object to * acquire on the specified zone * a_pid - [RO, *RO] - (pid_t) * Process i.d. to associate with this lock * == 0 - no process i.d. associated with the lock * a_wait - [RO, *RO] - (int) * Determines what to do if the lock cannot be acquired: * == B_TRUE - wait for the lock to be acquired * == B_FALSE - do not wait for the lock to be acquired * Returns: boolean_t * B_TRUE - lock acquired * B_FALSE - lock not acquired */ boolean_t _z_acquire_lock(char **r_lockKey, char *a_zoneName, char *a_lockObject, pid_t a_pid, boolean_t a_wait) { argArray_t *args; boolean_t b; char *adjustedLockObject = (char *)NULL; char *p; char *results = (char *)NULL; int r; int status; /* entry assertions */ assert(a_zoneName != (char *)NULL); assert(a_lockObject != (char *)NULL); assert(*a_lockObject != '\0'); assert(r_lockKey != (char **)NULL); /* entry debugging info */ _z_echoDebug(DBG_ZONES_APLK, a_zoneName, a_lockObject, a_pid); /* reset returned lock key handle */ *r_lockKey = (char *)NULL; /* * Only one lock file must ever be used - the one located on the root * file system of the currently running Solaris instance. To allow for * alternative roots to be properly locked, adjust the lock object to * take root path into account; if necessary, the root path will be * prepended to the lock object. */ b = _z_adjust_lock_object_for_rootpath(&adjustedLockObject, a_lockObject); if (!b) { return (B_FALSE); } /* * construct command arguments: * pkgadm lock -a -q -o adjustedLockObject [ -w -W timeout ] * [ -p a_pid -z zoneid ] */ args = _z_new_args(20); /* generate new arg list */ (void) _z_add_arg(args, PKGADM_CMD); /* pkgadm command */ (void) _z_add_arg(args, "lock"); /* lock sub-command */ (void) _z_add_arg(args, "-a"); /* acquire lock */ (void) _z_add_arg(args, "-q"); /* quiet (no extra messages) */ (void) _z_add_arg(args, "-o"); /* object to acquire */ (void) _z_add_arg(args, "%s", adjustedLockObject); /* add [ -w -W timeout ] if waiting for lock */ if (a_wait == B_TRUE) { (void) _z_add_arg(args, "-w"); /* wait */ (void) _z_add_arg(args, "-W"); /* wait timeout */ (void) _z_add_arg(args, "%ld", (long)MAX_RETRIES*RETRY_DELAY_SECS); } /* add process/zone i.d.s if process i.d. provided */ if (a_pid > 0) { (void) _z_add_arg(args, "-p"); /* lock valid process i.d. */ (void) _z_add_arg(args, "%lld", (long long)getpid()); (void) _z_add_arg(args, "-z"); /* lock valid zone i.d. */ (void) _z_add_arg(args, "%lld", (long long)getzoneid()); } /* execute command */ r = _z_zone_exec(&status, &results, (char *)NULL, PKGADM_CMD, _z_get_argv(args), a_zoneName, (int *)NULL); /* free generated argument list */ _z_free_args(args); /* return error if failed to acquire */ if ((r != 0) || (status != 0)) { _z_echoDebug(DBG_ZONES_APLK_EXIT, a_zoneName, adjustedLockObject, a_pid, r, status, results ? results : ""); /* free up results if returned */ if (results) { free(results); } /* free adjusted lock object */ free(adjustedLockObject); /* return failure */ return (B_FALSE); } /* return success if no results returned */ if (results == (char *)NULL) { return (B_TRUE); } /* return the lock key */ p = _z_strGetToken((char *)NULL, results, 0, "\n"); _z_strRemoveLeadingWhitespace(&p); *r_lockKey = p; /* exit debugging info */ _z_echoDebug(DBG_ZONES_APLK_RESULTS, a_zoneName, adjustedLockObject, p, results); /* free up results */ free(results); /* free adjusted lock object */ free(adjustedLockObject); /* return success */ return (B_TRUE); } /* * Name: _z_adjust_lock_object_for_rootpath * Description: Given a lock object and a root path, if the root path is not * the current running system root, then alter the lock object * to contain a reference to the root path. Only one lock file must * ever be used to create and maintain locks - the lock file that * is located in /tmp on the root file system of the currently * running Solaris instance. To allow for alternative roots to be * properly locked, if necessary adjust the lock object to take * root path into account. If the root path does not indicate the * current running Solaris instance, then the root path will be * prepended to the lock object. * Arguments: r_result - [RW, *RW] - (char **) * Pointer to handle to character string that will contain * the lock object to use. * a_lockObject - [RO, *RO] - (char *) * Pointer to string representing the lock object to adjust * Returns: boolean_t * B_TRUE - lock object adjusted and returned * B_FALSE - unable to adjust lock object * NOTE: Any string returned is placed in new storage for the * calling function. The caller must use 'free' to dispose * of the storage once the string is no longer needed. * * A lock object has this form: * * name.value [ /name.value [ /name.value ... ] ] * * The "value is either a specific object or a "*", for example: * * package.test * * This locks the package "test" * * zone.* /package.* * * This locks all packages on all zones. * * zone.* /package.SUNWluu * * This locks the package SUNWluu on all zones. * * If a -R rootpath is specified, since there is only one lock file in * the current /tmp, the lock object is modified to include the root * path: * * rootpath.rootpath/zone.* /package.* * * locks all packages on all zones in the root path "?" * * The characters "/" and "*" and "." cannot be part of the "value"; that * is if "-R /tmp/gmg*dir.test-path" is specified, the final object * cannot be: * * rootpath./tmp/gmg*dir.test-path/zone.* /package.* * * This would be parsed as: * * "rootpath." "/tmp" "gmg*dir.test-path" "zone.*" "package.*" * * which is not correct. * * So the path is modified by the loop, in this case it would result in * this lock object: * * rootpath.-1tmp-1gmg-3dir-2test---path/zone.* /package.* * * This is parsed as: * * "rootpath.-1tmp-1gmg-3dir-2test---path" "zone.*" "package.*" * * which is then interpreted as: * * "rootpath./tmp/gmg*dir.test-path" "zone.*" "package.*" */ boolean_t _z_adjust_lock_object_for_rootpath(char **r_result, char *a_lockObject) { char realRootPath[PATH_MAX] = {'\0'}; const char *a_rootPath; /* entry assertions */ assert(r_result != (char **)NULL); assert(a_lockObject != (char *)NULL); assert(*a_lockObject != '\0'); /* reset returned lock object handle */ *r_result = (char *)NULL; /* * if root path points to "/" return a duplicate of the passed in * lock objects; otherwise, resolve root path and adjust lock object by * prepending the rootpath to the lock object (using LOBJ_ROOTPATH). */ a_rootPath = _z_global_data._z_root_dir; if ((a_rootPath == (char *)NULL) || (*a_rootPath == '\0') || (strcmp(a_rootPath, "/") == 0)) { /* root path not specified or is only "/" - no -R specified */ *r_result = _z_strdup(a_lockObject); } else { /* * root path is not "" or "/" - -R to an alternative root has * been specified; resolve all symbolic links and relative nodes * of path name and determine absolute path to the root path. */ if (realpath(a_rootPath, realRootPath) == (char *)NULL) { /* cannot determine absolute path; use path specified */ (void) strlcpy(realRootPath, a_rootPath, sizeof (realRootPath)); } /* * if root path points to "/" duplicate existing lock object; * otherwise, resolve root path and adjust lock object by * prepending the rootpath to the lock object */ if (strcmp(realRootPath, "/") == 0) { *r_result = _z_strdup(a_lockObject); } else { char *p1, *p2, *p3; /* prefix out /.* which cannot be part of lock object */ p1 = _z_calloc((strlen(realRootPath)*2)+1); for (p3 = p1, p2 = realRootPath; *p2 != '\0'; p2++) { switch (*p2) { case '/': /* / becomes -1 */ *p3++ = '-'; *p3++ = '1'; break; case '.': /* . becomes -2 */ *p3++ = '-'; *p3++ = '2'; break; case '*': /* * becomes -3 */ *p3++ = '-'; *p3++ = '3'; break; case '-': /* - becomes -- */ *p3++ = '-'; *p3++ = '-'; break; default: /* do not prefix out char */ *p3++ = *p2; break; } } /* create "realpath.%s" object */ p2 = _z_strPrintf(LOBJ_ROOTPATH, p1); free(p1); if (p2 == (char *)NULL) { _z_program_error(ERR_MALLOC, "", errno, strerror(errno)); return (B_FALSE); } /* create "realpath.%s/..." final lock object */ *r_result = _z_strPrintf("%s/%s", p2, a_lockObject); free(p2); if (*r_result == (char *)NULL) { _z_program_error(ERR_MALLOC, "", errno, strerror(errno)); return (B_FALSE); } } } /* exit debugging info */ _z_echoDebug(DBG_ZONES_ADJLCKOBJ_EXIT, a_lockObject, *r_result, a_rootPath ? a_rootPath : "", realRootPath ? realRootPath : ""); /* return success */ return (B_TRUE); } /* * Name: _z_lock_zone * Description: Acquire specified locks on specified zone * Arguments: a_zlst - [RO, *RW] - (zoneListElement_t *) * Pointer to zone list structure element describing * the zone the lock - the structure is updated with * the lock objects and keys if the locks are acquired * a_lflags - [RO, *RO] - (ZLOCKS_T) * Flags indicating which locks to acquire on the zone * Returns: boolean_t * == B_TRUE - locks successfully acquired * == B_FALSE - failed to acquire the locks */ boolean_t _z_lock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags) { char *scratchName; boolean_t b; /* entry assertions */ assert(a_zlst != (zoneListElement_t *)NULL); /* entry debugging info */ _z_echoDebug(DBG_ZONES_LCK_ZONE, a_zlst->_zlName, a_lflags); scratchName = a_zlst->_zlScratchName == NULL ? a_zlst->_zlName : a_zlst->_zlScratchName; /* * acquire zone lock */ if (a_lflags & ZLOCKS_ZONE_ADMIN) { /* * lock zone administration if not already locked * if the lock cannot be released, stop and return an error */ _z_echoDebug(DBG_ZONES_LCK_ZONE_ZONEADM, a_zlst->_zlName, LOBJ_ZONEADMIN); b = _z_lock_zone_object(&a_zlst->_zlLockObjects, scratchName, LOBJ_ZONEADMIN, (pid_t)0, MSG_ZONES_LCK_ZONE_ZONEADM, ERR_ZONES_LCK_ZONE_ZONEADM); if (b == B_FALSE) { return (b); } } /* * acquire package lock */ if (a_lflags & ZLOCKS_PKG_ADMIN) { /* * zone administration is locked; lock package administration if * not already locked; if the lock cannot be released, stop, * release the zone administration lock and return an error */ _z_echoDebug(DBG_ZONES_LCK_ZONE_PKGADM, a_zlst->_zlName, LOBJ_PKGADMIN); b = _z_lock_zone_object(&a_zlst->_zlLockObjects, scratchName, LOBJ_PKGADMIN, (pid_t)0, MSG_ZONES_LCK_ZONE_PKGADM, ERR_ZONES_LCK_ZONE_PKGADM); if (b == B_FALSE) { (void) _z_unlock_zone(a_zlst, a_lflags); return (b); } } /* * all locks have been obtained - return success! */ return (B_TRUE); } /* * Name: _z_lock_zone_object * Description: lock a single lock object in a specified zone * Arguments: r_objectLocks - [RW, *RW] - (char **) * Pointer to handle to character string containing a list * of all objects locked for this zone - this string will * have the key to release the specified object added to it * if the lock is acquired. * a_zoneName - [RO, *RO] - (char *) * Pointer to string representing the name of the zone to * acquire the specified lock on * a_lockObject - [RO, *RO] - (char *) * Pointer to string representing the lock object to * acquire on the specified zone * a_pid - [RO, *RO] - (pid_t) * Process i.d. to associate with this lock * == 0 - no process i.d. associated with the lock * a_waitingMsg - [RO, *RO] - (char *) * Localized message to be output if waiting for the lock * because the lock cannot be immediately be acquired * a_busyMsg - [RO, *RO] - (char *) * Localized message to be output if the lock cannot be * released * Returns: boolean_t * B_TRUE - lock released * B_FALSE - lock not released */ boolean_t _z_lock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject, pid_t a_pid, char *a_waitingMsg, char *a_busyMsg) { boolean_t b; char *p = (char *)NULL; char lockItem[LOCK_OBJECT_MAXLEN+LOCK_KEY_MAXLEN+4]; char lockKey[LOCK_KEY_MAXLEN+2]; char lockObject[LOCK_OBJECT_MAXLEN+2]; int i; /* entry assertions */ assert(r_objectLocks != (char **)NULL); assert(a_zoneName != (char *)NULL); assert(a_waitingMsg != (char *)NULL); assert(a_busyMsg != (char *)NULL); assert(a_lockObject != (char *)NULL); assert(*a_lockObject != '\0'); /* entry debugging info */ _z_echoDebug(DBG_ZONES_LCK_OBJ, a_lockObject, a_zoneName, a_pid, *r_objectLocks ? *r_objectLocks : ""); /* if lock objects held search for object to lock */ if (*r_objectLocks != (char *)NULL) { for (i = 0; ; i++) { /* get next object locked on this zone */ _z_strGetToken_r((char *)NULL, *r_objectLocks, i, "\n", lockItem, sizeof (lockItem)); /* break out of loop if no more locks in list */ if (lockItem[0] == '\0') { _z_echoDebug(DBG_ZONES_LCK_OBJ_NOTHELD, a_lockObject, a_zoneName); break; } /* get object and key for this lock */ _z_strGetToken_r((char *)NULL, lockItem, 0, "\t", lockObject, sizeof (lockObject)); _z_strGetToken_r((char *)NULL, lockItem, 1, "\t", lockKey, sizeof (lockKey)); /* return success if the lock is held */ if (strcmp(lockObject, a_lockObject) == 0) { _z_echoDebug(DBG_ZONES_LCK_OBJ_FOUND, lockObject, lockKey); return (B_TRUE); } /* not the object to lock - scan next object */ _z_echoDebug(DBG_ZONES_LCK_OBJ_NOTFOUND, lockObject, lockKey); } } /* * the object to lock is not held - acquire the lock */ /* acquire object with no wait */ b = _z_acquire_lock(&p, a_zoneName, a_lockObject, a_pid, B_FALSE); if (b == B_FALSE) { /* failure - output message and acquire with wait */ _z_echo(a_waitingMsg, (long)MAX_RETRIES*RETRY_DELAY_SECS, a_zoneName, _z_global_data._z_root_dir); b = _z_acquire_lock(&p, a_zoneName, a_lockObject, a_pid, B_TRUE); } /* output error message and return failure if both acquires failed */ if (b == B_FALSE) { _z_program_error(a_busyMsg, a_zoneName); return (b); } /* add object/key to held locks */ _z_strPrintf_r(lockItem, sizeof (lockItem), "%s\t%s", a_lockObject, p); _z_strAddToken(r_objectLocks, lockItem, '\n'); free(p); /* return success */ return (B_TRUE); } /* * Name: _z_release_lock * Description: release a lock held on a zone * Arguments: a_zoneName - [RO, *RO] - (char *) * Pointer to string representing the name of the zone to * release the specified lock on * a_lockObject - [RO, *RO] - (char *) * Pointer to string representing the lock object to * release on the specified zone * a_lockKey - [RO, *RO] - (char *) * Pointer to string representing the lock key associated * with the lock object to be released - this key is * returned when the lock is acquired and must be provided * when releasing the lock * a_wait - [RO, *RO] - (int) * Determines what to do if the lock cannot be released: * == B_TRUE - wait for the lock to be released * == B_FALSE - do not wait for the lock to be released * Returns: boolean_t * B_TRUE - lock released * B_FALSE - lock not released */ boolean_t _z_release_lock(char *a_zoneName, char *a_lockObject, char *a_lockKey, boolean_t a_wait) { argArray_t *args; boolean_t b; char *adjustedLockObject = (char *)NULL; char *results = (char *)NULL; int r; int status; /* entry assertions */ assert(a_zoneName != (char *)NULL); assert(a_lockObject != (char *)NULL); assert(*a_lockObject != '\0'); assert(a_lockKey != (char *)NULL); assert(*a_lockKey != '\0'); /* entry debugging info */ _z_echoDebug(DBG_ZONES_RELK, a_zoneName, a_lockObject, a_lockKey ? a_lockKey : ""); /* * Only one lock file must ever be used - the one located on the root * file system of the currently running Solaris instance. To allow for * alternative roots to be properly locked, adjust the lock object to * take root path into account; if necessary, the root path will be * prepended to the lock object. */ b = _z_adjust_lock_object_for_rootpath(&adjustedLockObject, a_lockObject); if (!b) { return (B_FALSE); } /* * construct command arguments: * pkgadm lock -r -o adjustedLockObject -k a_lockKey [-w -W timeout] */ args = _z_new_args(20); /* generate new arg list */ (void) _z_add_arg(args, PKGADM_CMD); /* pkgadm command */ (void) _z_add_arg(args, "lock"); /* lock sub-command */ (void) _z_add_arg(args, "-r"); /* release lock */ (void) _z_add_arg(args, "-o"); /* object to release */ (void) _z_add_arg(args, "%s", adjustedLockObject); (void) _z_add_arg(args, "-k"); /* object's key */ (void) _z_add_arg(args, "%s", a_lockKey); /* add [ -w -W timeout ] if waiting for lock */ if (a_wait == B_TRUE) { (void) _z_add_arg(args, "-w"); /* wait */ (void) _z_add_arg(args, "-W"); /* wait timeout */ (void) _z_add_arg(args, "%ld", (long)MAX_RETRIES*RETRY_DELAY_SECS); } /* execute command */ r = _z_zone_exec(&status, &results, (char *)NULL, PKGADM_CMD, _z_get_argv(args), a_zoneName, (int *)NULL); /* free generated argument list */ _z_free_args(args); /* exit debugging info */ _z_echoDebug(DBG_ZONES_RELK_EXIT, adjustedLockObject, a_lockKey, a_zoneName, r, status, results ? results : ""); /* free adjusted lock object */ free(adjustedLockObject); free(results); return (((r == 0) && (status == 0)) ? B_TRUE : B_FALSE); } /* * Name: _z_unlock_zone * Description: Released specified locks on specified zone * Arguments: a_zlst - [RO, *RW] - (zoneListElement_t *) * Pointer to zone list structure element describing * the zone the unlock - the structure is updated by * removing the lock object and key if the locks are * successfully released * a_lflags - [RO, *RO] - (ZLOCKS_T) * Flags indicating which locks to release on the zone * Returns: boolean_t * == B_TRUE - locks successfully released * == B_FALSE - failed to release the locks */ boolean_t _z_unlock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags) { char *scratchName; boolean_t b; boolean_t errors = B_FALSE; /* entry assertions */ assert(a_zlst != (zoneListElement_t *)NULL); /* entry debugging info */ _z_echoDebug(DBG_ZONES_ULK_ZONE, a_zlst->_zlName, a_lflags); scratchName = a_zlst->_zlScratchName == NULL ? a_zlst->_zlName : a_zlst->_zlScratchName; if (a_lflags & ZLOCKS_PKG_ADMIN) { /* * if locked, unlock package administration lock * if the lock cannot be released, continue anyway */ _z_echoDebug(DBG_ZONES_ULK_ZONE_PKGADM, a_zlst->_zlName, LOBJ_PKGADMIN); b = _z_unlock_zone_object(&a_zlst->_zlLockObjects, scratchName, LOBJ_PKGADMIN, WRN_ZONES_ULK_ZONE_PKGADM); if (b == B_FALSE) { errors = B_TRUE; } } if (a_lflags & ZLOCKS_ZONE_ADMIN) { /* * if locked, unlock zone administration lock * if the lock cannot be released, continue anyway */ _z_echoDebug(DBG_ZONES_ULK_ZONE_ZONEADM, a_zlst->_zlName, LOBJ_ZONEADMIN); b = _z_unlock_zone_object(&a_zlst->_zlLockObjects, scratchName, LOBJ_ZONEADMIN, WRN_ZONES_ULK_ZONE_ZONEADM); if (b == B_FALSE) { errors = B_TRUE; } } return (!errors); } /* * Name: _z_unlock_zone_object * Description: unlock a single lock object in a specified zone * Arguments: r_objectLocks - [RW, *RW] - (char **) * Pointer to handle to character string containing a list * of all objects locked for this zone - this string must * contain the key to release the specified object - if not * then the lock is not released - if so then the lock is * released and the key is removed from this list. * a_zoneName - [RO, *RO] - (char *) * Pointer to string representing the name of the zone to * release the specified lock on * a_lockObject - [RO, *RO] - (char *) * Pointer to string representing the lock object to * release on the specified zone * a_errMsg - [RO, *RO] - (char *) * Localized message to be output if the lock cannot be * released * Returns: boolean_t * B_TRUE - lock released * B_FALSE - lock not released */ boolean_t _z_unlock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject, char *a_errMsg) { boolean_t b; char lockItem[LOCK_OBJECT_MAXLEN+LOCK_KEY_MAXLEN+4]; char lockKey[LOCK_KEY_MAXLEN+2]; char lockObject[LOCK_OBJECT_MAXLEN+2]; int i; /* entry assertions */ assert(r_objectLocks != (char **)NULL); assert(a_zoneName != (char *)NULL); assert(a_errMsg != (char *)NULL); assert(a_lockObject != (char *)NULL); assert(*a_lockObject != '\0'); /* entry debugging info */ _z_echoDebug(DBG_ZONES_ULK_OBJ, a_lockObject, a_zoneName, *r_objectLocks ? *r_objectLocks : ""); /* return success if no objects are locked */ if (*r_objectLocks == (char *)NULL) { _z_echoDebug(DBG_ZONES_ULK_OBJ_NONE, a_zoneName); return (B_TRUE); } /* see if the specified lock is held on this zone */ for (i = 0; ; i++) { /* get next object locked on this zone */ _z_strGetToken_r((char *)NULL, *r_objectLocks, i, "\n", lockItem, sizeof (lockItem)); /* return success if no more objects locked */ if (lockItem[0] == '\0') { _z_echoDebug(DBG_ZONES_ULK_OBJ_NOTHELD, a_lockObject, a_zoneName); return (B_TRUE); } /* get object and key for this lock */ _z_strGetToken_r((char *)NULL, lockItem, 0, "\t", lockObject, sizeof (lockObject)); _z_strGetToken_r((char *)NULL, lockItem, 1, "\t", lockKey, sizeof (lockKey)); /* break out of loop if object is the one to unlock */ if (strcmp(lockObject, a_lockObject) == 0) { _z_echoDebug(DBG_ZONES_ULK_OBJ_FOUND, lockObject, lockKey); break; } /* not the object to unlock - scan next object */ _z_echoDebug(DBG_ZONES_ULK_OBJ_NOTFOUND, lockObject, lockKey); } /* * the object to unlock is held - release the lock */ /* release object with wait */ b = _z_release_lock(a_zoneName, a_lockObject, lockKey, B_TRUE); if (b == B_FALSE) { /* failure - issue error message and return failure */ _z_program_error(a_errMsg, a_zoneName); return (b); } /* remove object/key from held locks */ _z_strRemoveToken(r_objectLocks, lockItem, "\n", 0); /* return success */ return (B_TRUE); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "zones_strings.h" #include "instzones_lib.h" #define MNTTAB "/etc/mnttab" #define MNTTAB_HUNK 32 static struct mnttab *mountTable; static size_t mountTableSize = 0; static boolean_t createdFlag = B_FALSE; /* * Name : z_createMountTable * Description : Populate the mountTable Array with mnttab entries * Arguments : void * Returns : int * 0: The Mount Table was succesfully initialized * -1: There was an error during initialisation */ int z_createMountTable(void) { FILE *fp; struct mnttab ent; struct mnttab *entp; if (createdFlag) { return (0); } fp = fopen(MNTTAB, "r"); if (fp == NULL) { _z_program_error(ERR_OPEN_READ, MNTTAB, errno, strerror(errno)); return (-1); } /* Put the entries into the table */ mountTable = NULL; mountTableSize = 0; createdFlag = B_TRUE; while (getmntent(fp, &ent) == 0) { if (mountTableSize % MNTTAB_HUNK == 0) { mountTable = _z_realloc(mountTable, (mountTableSize + MNTTAB_HUNK) * sizeof (ent)); } entp = &mountTable[mountTableSize++]; /* * Zero out any fields we're not using. */ (void) memset(entp, 0, sizeof (*entp)); if (ent.mnt_special != NULL) entp->mnt_special = _z_strdup(ent.mnt_special); if (ent.mnt_mntopts != NULL) entp->mnt_mntopts = _z_strdup(ent.mnt_mntopts); entp->mnt_mountp = _z_strdup(ent.mnt_mountp); entp->mnt_fstype = _z_strdup(ent.mnt_fstype); } (void) fclose(fp); return (0); } /* * Name : findPathRWStatus * Description : Check whether the given path is an mnttab entry * Arguments : char * - The Path to be verified * Returns : int * -1: The Path is NOT present in the table (mnttab) * 0: The Path is present in the table and is mounted read-only * 1: The Path is present in the table and is mounted read-write */ static int findPathRWStatus(const char *a_path) { int i; for (i = 0; i < mountTableSize; i++) { if (strcmp(a_path, mountTable[i].mnt_mountp) == 0) { if (hasmntopt(&mountTable[i], MNTOPT_RO) != NULL) { return (0); } else { return (1); } } } return (-1); } /* * Name : z_isPathWritable * Description : Check if the given path is in a writable area * Arguments : char * - The Path to be verified * Returns : int * 0: The Path is under a read-only mount * 1: The Path is under a read-write mount * NOTE : This funcion automatically initialises * the mountPoint table if needed. */ int z_isPathWritable(const char *a_str) { int i, result, slen; char a_path[MAXPATHLEN]; if (!createdFlag) { if (z_createMountTable() != 0) { return (1); } } (void) strlcpy(a_path, a_str, sizeof (a_path)); slen = strlen(a_path); /* * This for loop traverses Path backwards, incrementally removing the * basename of Path and looking for the resultant directory in the * mnttab. Once found, it returns the rw status of that file system. */ for (i = slen; i > 0; i--) { if ((a_path[i] == '/') || (a_path[i] == '\0')) { a_path[i] = '\0'; result = findPathRWStatus(a_path); if (result != -1) { return (result); } } } return (1); } /* * Name : z_destroyMountTable * Description : Clear the entries in the mount table * Arguments : void * Returns : void */ void z_destroyMountTable(void) { int i; if (!createdFlag) { return; } if (mountTable == NULL) { return; } for (i = 0; i < mountTableSize; i++) { free(mountTable[i].mnt_mountp); free(mountTable[i].mnt_fstype); free(mountTable[i].mnt_special); free(mountTable[i].mnt_mntopts); assert(mountTable[i].mnt_time == NULL); } free(mountTable); mountTable = NULL; mountTableSize = 0; createdFlag = B_FALSE; } /* * Name : z_resolve_lofs * Description : Loop over potential loopback mounts and symlinks in a * given path and resolve them all down to an absolute path. * Arguments : char * - path to resolve. path is in writable storage. * size_t - length of path storage. * Returns : void */ void z_resolve_lofs(char *path, size_t pathlen) { int len, arlen, i; const char *altroot; char tmppath[MAXPATHLEN]; boolean_t outside_altroot; if ((len = resolvepath(path, tmppath, sizeof (tmppath))) == -1) return; tmppath[len] = '\0'; (void) strlcpy(path, tmppath, pathlen); if (z_createMountTable() == -1) return; altroot = zonecfg_get_root(); arlen = strlen(altroot); outside_altroot = B_FALSE; for (;;) { struct mnttab *mnp; /* Search in reverse order to find longest match */ for (i = mountTableSize; i > 0; i--) { mnp = &mountTable[i - 1]; if (mnp->mnt_fstype == NULL || mnp->mnt_mountp == NULL || mnp->mnt_special == NULL) continue; len = strlen(mnp->mnt_mountp); if (strncmp(mnp->mnt_mountp, path, len) == 0 && (path[len] == '/' || path[len] == '\0')) break; } if (i <= 0) break; /* If it's not a lofs then we're done */ if (strcmp(mnp->mnt_fstype, MNTTYPE_LOFS) != 0) break; if (outside_altroot) { char *cp; int olen = sizeof (MNTOPT_RO) - 1; /* * If we run into a read-only mount outside of the * alternate root environment, then the user doesn't * want this path to be made read-write. */ if (mnp->mnt_mntopts != NULL && (cp = strstr(mnp->mnt_mntopts, MNTOPT_RO)) != NULL && (cp == mnp->mnt_mntopts || cp[-1] == ',') && (cp[olen] == '\0' || cp[olen] == ',')) { break; } } else if (arlen > 0 && (strncmp(mnp->mnt_special, altroot, arlen) != 0 || (mnp->mnt_special[arlen] != '\0' && mnp->mnt_special[arlen] != '/'))) { outside_altroot = B_TRUE; } /* use temporary buffer because new path might be longer */ (void) snprintf(tmppath, sizeof (tmppath), "%s%s", mnp->mnt_special, path + len); if ((len = resolvepath(tmppath, path, pathlen)) == -1) break; path[len] = '\0'; } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" #define isdot(x) ((x[0] == '.') && (!x[1] || (x[1] == '/'))) #define isdotdot(x) ((x[0] == '.') && (x[1] == '.') && \ (!x[2] || (x[2] == '/'))) /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: z_make_zone_root * Description: Given its zonepath, generate a string representing the * mountpoint of where the root path for a nonglobal zone is * mounted. The zone is mounted using 'zoneadm', which mounts * the zone's filesystems wrt /lu/a * Arguments: zone_path - non-NULL pointer to string representing zonepath * Returns: char * - pointer to string representing zonepath of zone * NULL - if zone_path is NULL. * Notes: The string returned is in static storage and should not be * free()ed by the caller. */ char * z_make_zone_root(char *zone_path) { static char zone_root_buf[MAXPATHLEN]; if (zone_path == NULL) return (NULL); (void) snprintf(zone_root_buf, MAXPATHLEN, "%s%slu/a", zone_path, (zone_path[0] != '\0' && zone_path[strlen(zone_path) - 1] == '/') ? "" : "/"); return (zone_root_buf); } void z_path_canonize(char *a_file) { char *pt; char *last; int level; /* remove references such as "./" and "../" and "//" */ for (pt = a_file; *pt; /* void */) { if (isdot(pt)) { (void) strcpy(pt, pt[1] ? pt+2 : pt+1); } else if (isdotdot(pt)) { level = 0; last = pt; do { level++; last += 2; if (*last) { last++; } } while (isdotdot(last)); --pt; /* point to previous '/' */ while (level--) { if (pt <= a_file) { return; } while ((*--pt != '/') && (pt > a_file)) ; } if (*pt == '/') { pt++; } (void) strcpy(pt, last); } else { while (*pt && (*pt != '/')) { pt++; } if (*pt == '/') { while (pt[1] == '/') { (void) strcpy(pt, pt+1); } pt++; } } } if ((--pt > a_file) && (*pt == '/')) { *pt = '\0'; } } void z_canoninplace(char *src) { char *dst; char *src_start; /* keep a ptr to the beginning of the src string */ src_start = src; dst = src; while (*src) { if (*src == '/') { *dst++ = '/'; while (*src == '/') src++; } else *dst++ = *src++; } /* * remove any trailing slashes, unless the whole string is just "/". * If the whole string is "/" (i.e. if the last '/' cahr in dst * in the beginning of the original string), just terminate it * and return "/". */ if ((*(dst - 1) == '/') && ((dst - 1) != src_start)) dst--; *dst = '\0'; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Module: zones_states.c * Group: libinstzones * Description: Provide "zones" state interfaces for install consolidation code * * Public Methods: * * z_make_zone_running - change state of non-global zone to "running" * _z_make_zone_ready - change state of non-global zone to "ready" * _z_make_zone_down - change state of non-global zone to "down" */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ /* * Library Function Prototypes */ /* * Local Function Prototypes */ /* * global internal (private) declarations */ /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: _z_make_zone_running * Description: Given a zone element entry for the non-global zone to affect, * change the state of that non-global zone to "running" * Arguments: a_zlem - [RO, *RW] - (zoneListElement_t) * Zone list element describing the non-global zone to * make running * Returns: boolean_t * B_TRUE - non-global zone state changed successfully * B_FALSE - failed to make the non-global zone run */ boolean_t _z_make_zone_running(zoneListElement_t *a_zlem) { FILE *fp; argArray_t *args; char zonename[ZONENAME_MAX]; char *results = (char *)NULL; int ret; int status = 0; /* entry assertions */ assert(a_zlem != NULL); /* act based on the zone's current kernel state */ switch (a_zlem->_zlCurrKernelStatus) { case ZONE_STATE_RUNNING: case ZONE_STATE_MOUNTED: /* already running */ return (B_TRUE); case ZONE_STATE_READY: /* This should never happen */ if (zonecfg_in_alt_root()) return (B_FALSE); /* * We're going to upset the zone anyway, so might as well just * halt it now and fall through to normal mounting. */ _z_echoDebug(DBG_TO_ZONEHALT, a_zlem->_zlName); args = _z_new_args(5); /* generate new arg list */ (void) _z_add_arg(args, ZONEADM_CMD); (void) _z_add_arg(args, "-z"); (void) _z_add_arg(args, a_zlem->_zlName); (void) _z_add_arg(args, "halt"); ret = z_ExecCmdArray(&status, &results, (char *)NULL, ZONEADM_CMD, _z_get_argv(args)); /* free generated argument list */ _z_free_args(args); if (ret != 0) { _z_program_error(ERR_ZONEHALT_EXEC, ZONEADM_CMD, strerror(errno)); free(results); return (B_FALSE); } if (status != 0) { if (status == -1) { _z_program_error(ERR_ZONEBOOT_CMD_SIGNAL, ZONEADM_CMD, a_zlem->_zlName); } else { _z_program_error(ERR_ZONEBOOT_CMD_ERROR, ZONEADM_CMD, a_zlem->_zlName, status, results == NULL ? "" : "\n", results == NULL ? "" : results); } free(results); return (B_FALSE); } free(results); a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED; /* FALLTHROUGH */ case ZONE_STATE_INSTALLED: case ZONE_STATE_DOWN: /* return false if the zone cannot be booted */ if (a_zlem->_zlStatus & ZST_NOT_BOOTABLE) { return (B_FALSE); } _z_echoDebug(DBG_TO_ZONERUNNING, a_zlem->_zlName); /* these states can be booted - do so */ args = _z_new_args(10); /* generate new arg list */ (void) _z_add_arg(args, ZONEADM_CMD); if (zonecfg_in_alt_root()) { (void) _z_add_arg(args, "-R"); (void) _z_add_arg(args, "%s", (char *)zonecfg_get_root()); } (void) _z_add_arg(args, "-z"); (void) _z_add_arg(args, "%s", a_zlem->_zlName); (void) _z_add_arg(args, "mount"); ret = z_ExecCmdArray(&status, &results, (char *)NULL, ZONEADM_CMD, _z_get_argv(args)); /* free generated argument list */ _z_free_args(args); if (ret != 0) { _z_program_error(ERR_ZONEBOOT_EXEC, ZONEADM_CMD, strerror(errno)); free(results); return (B_FALSE); } if (status != 0) { if (status == -1) { _z_program_error(ERR_ZONEBOOT_CMD_SIGNAL, ZONEADM_CMD, a_zlem->_zlName); } else { _z_program_error(ERR_ZONEBOOT_CMD_ERROR, ZONEADM_CMD, a_zlem->_zlName, status, results == NULL ? "" : "\n", results == NULL ? "" : results); } free(results); /* remember this zone cannot be booted */ a_zlem->_zlStatus |= ZST_NOT_BOOTABLE; return (B_FALSE); } free(results); if (zonecfg_in_alt_root()) { if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL || zonecfg_find_scratch(fp, a_zlem->_zlName, zonecfg_get_root(), zonename, sizeof (zonename)) == -1) { _z_program_error(ERR_ZONEBOOT_DIDNT_BOOT, a_zlem->_zlName); if (fp != NULL) zonecfg_close_scratch(fp); return (B_FALSE); } zonecfg_close_scratch(fp); free(a_zlem->_zlScratchName); a_zlem->_zlScratchName = _z_strdup(zonename); } a_zlem->_zlCurrKernelStatus = ZONE_STATE_MOUNTED; return (B_TRUE); case ZONE_STATE_CONFIGURED: case ZONE_STATE_INCOMPLETE: case ZONE_STATE_SHUTTING_DOWN: default: /* cannot transition (boot) these states */ return (B_FALSE); } } /* * Name: _z_make_zone_ready * Description: Given a zone element entry for the non-global zone to affect, * restore the ready state of the zone when the zone is currently * in the running state. * Arguments: a_zlem - [RO, *RW] - (zoneListElement_t) * Zone list element describing the non-global zone to * make ready * Returns: boolean_t * B_TRUE - non-global zone state changed successfully * B_FALSE - failed to make the non-global zone ready */ boolean_t _z_make_zone_ready(zoneListElement_t *a_zlem) { argArray_t *args; char *results = (char *)NULL; int status = 0; int i; int ret; zone_state_t st; /* entry assertions */ assert(a_zlem != (zoneListElement_t *)NULL); /* act based on the zone's current kernel state */ switch (a_zlem->_zlCurrKernelStatus) { case ZONE_STATE_DOWN: case ZONE_STATE_READY: /* already down */ return (B_TRUE); case ZONE_STATE_MOUNTED: _z_echoDebug(DBG_TO_ZONEUNMOUNT, a_zlem->_zlName); args = _z_new_args(10); /* generate new arg list */ (void) _z_add_arg(args, ZONEADM_CMD); (void) _z_add_arg(args, "-z"); (void) _z_add_arg(args, "%s", a_zlem->_zlName); (void) _z_add_arg(args, "unmount"); ret = z_ExecCmdArray(&status, &results, NULL, ZONEADM_CMD, _z_get_argv(args)); if (ret != 0) { _z_program_error(ERR_ZONEUNMOUNT_EXEC, ZONEADM_CMD, strerror(errno)); free(results); _z_free_args(args); return (B_FALSE); } if (status != 0) { if (status == -1) { _z_program_error(ERR_ZONEUNMOUNT_CMD_SIGNAL, ZONEADM_CMD, a_zlem->_zlName); } else { _z_program_error(ERR_ZONEUNMOUNT_CMD_ERROR, ZONEADM_CMD, a_zlem->_zlName, status, results == NULL ? "" : "\n", results == NULL ? "" : results); } if (results != NULL) { free(results); } _z_free_args(args); return (B_FALSE); } if (results != NULL) { free(results); } _z_free_args(args); a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED; _z_echoDebug(DBG_TO_ZONEREADY, a_zlem->_zlName); args = _z_new_args(10); /* generate new arg list */ (void) _z_add_arg(args, ZONEADM_CMD); (void) _z_add_arg(args, "-z"); (void) _z_add_arg(args, "%s", a_zlem->_zlName); (void) _z_add_arg(args, "ready"); ret = z_ExecCmdArray(&status, &results, NULL, ZONEADM_CMD, _z_get_argv(args)); if (ret != 0) { _z_program_error(ERR_ZONEREADY_EXEC, ZONEADM_CMD, strerror(errno)); free(results); _z_free_args(args); return (B_FALSE); } if (status != 0) { _z_program_error(ERR_ZONEREADY_CMDFAIL, ZONEADM_CMD, a_zlem->_zlName, strerror(errno), results == NULL ? "" : "\n", results == NULL ? "" : results); if (results != NULL) { free(results); } _z_free_args(args); return (B_FALSE); } if (results != NULL) { free(results); } /* success - zone is now in the ready state */ a_zlem->_zlCurrKernelStatus = ZONE_STATE_READY; return (B_TRUE); case ZONE_STATE_RUNNING: _z_echoDebug(DBG_TO_ZONEREADY, a_zlem->_zlName); args = _z_new_args(10); /* generate new arg list */ (void) _z_add_arg(args, ZONEADM_CMD); (void) _z_add_arg(args, "-z"); (void) _z_add_arg(args, "%s", a_zlem->_zlName); (void) _z_add_arg(args, "ready"); ret = z_ExecCmdArray(&status, &results, (char *)NULL, ZONEADM_CMD, _z_get_argv(args)); /* free generated argument list */ _z_free_args(args); if (ret != 0) { _z_program_error(ERR_ZONEREADY_EXEC, ZONEADM_CMD, strerror(errno)); free(results); _z_free_args(args); return (B_FALSE); } if (status != 0) { _z_program_error(ERR_ZONEREADY_CMDFAIL, ZONEADM_CMD, a_zlem->_zlName, strerror(errno), results == (char *)NULL ? "" : "\n", results == (char *)NULL ? "" : results); if (results != (char *)NULL) { (void) free(results); } return (B_FALSE); } if (results != (char *)NULL) { (void) free(results); } for (i = 0; i < MAX_RETRIES; i++) { if (zone_get_state(a_zlem->_zlName, &st) != Z_OK) { break; } if ((st == ZONE_STATE_DOWN) || (st == ZONE_STATE_INSTALLED)|| (st == ZONE_STATE_READY)) { break; } (void) sleep(RETRY_DELAY_SECS); } /* failure if maximum retries reached */ if (i >= MAX_RETRIES) { _z_program_error(ERR_ZONEREADY_DIDNT_READY, a_zlem->_zlName); a_zlem->_zlCurrKernelStatus = st; return (B_FALSE); } /* success - zone is now in the ready state */ a_zlem->_zlCurrKernelStatus = ZONE_STATE_READY; return (B_TRUE); case ZONE_STATE_INSTALLED: case ZONE_STATE_CONFIGURED: case ZONE_STATE_INCOMPLETE: case ZONE_STATE_SHUTTING_DOWN: default: return (B_FALSE); } } /* * Name: _z_make_zone_down * Description: Given a zone element entry for the non-global zone to affect, * change the state of that non-global zone to "down" * Arguments: a_zlem - [RO, *RW] - (zoneListElement_t) * Zone list element describing the non-global zone to * make down * Returns: boolean_t * B_TRUE - non-global zone state changed successfully * B_FALSE - failed to make the non-global zone down */ boolean_t _z_make_zone_down(zoneListElement_t *a_zlem) { argArray_t *args; char *results = (char *)NULL; int status = 0; int ret; /* entry assertions */ assert(a_zlem != NULL); /* act based on the zone's current kernel state */ switch (a_zlem->_zlCurrKernelStatus) { case ZONE_STATE_DOWN: case ZONE_STATE_READY: case ZONE_STATE_RUNNING: /* shouldn't be touched */ return (B_TRUE); case ZONE_STATE_MOUNTED: _z_echoDebug(DBG_TO_ZONEHALT, a_zlem->_zlName); /* these states can be halted - do so */ args = _z_new_args(10); /* generate new arg list */ (void) _z_add_arg(args, ZONEADM_CMD); if (zonecfg_in_alt_root()) { (void) _z_add_arg(args, "-R"); (void) _z_add_arg(args, "%s", (char *)zonecfg_get_root()); } (void) _z_add_arg(args, "-z"); (void) _z_add_arg(args, "%s", a_zlem->_zlName); (void) _z_add_arg(args, "unmount"); ret = z_ExecCmdArray(&status, &results, (char *)NULL, ZONEADM_CMD, _z_get_argv(args)); /* free generated argument list */ _z_free_args(args); if (ret != 0) { _z_program_error(ERR_ZONEHALT_EXEC, ZONEADM_CMD, strerror(errno)); free(results); return (B_FALSE); } if (status != 0) { if (status == -1) { _z_program_error(ERR_ZONEBOOT_CMD_SIGNAL, ZONEADM_CMD, a_zlem->_zlName); } else { _z_program_error(ERR_ZONEBOOT_CMD_ERROR, ZONEADM_CMD, a_zlem->_zlName, status, results == NULL ? "" : "\n", results == NULL ? "" : results); } free(results); return (B_FALSE); } free(results); a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED; /* * Leave the scratch name in place because the upper level * software may have used it to construct file names and the * like. */ return (B_TRUE); case ZONE_STATE_INSTALLED: case ZONE_STATE_CONFIGURED: case ZONE_STATE_INCOMPLETE: case ZONE_STATE_SHUTTING_DOWN: default: return (B_FALSE); } } /* * Function: UmountAllZones * Description: Unmount all mounted zones under a specified directory. * * Scope: public * Parameters: mntpnt [RO, *RO] * Non-NULL pointer to name of directory to be unmounted. * Return: 0 - successfull * -1 - unmount failed; see errno for reason */ int UmountAllZones(char *mntpnt) { zoneList_t zlst; int k; int ret = 0; if (z_zones_are_implemented()) { z_set_zone_root(mntpnt); zlst = z_get_nonglobal_zone_list(); if (zlst == (zoneList_t)NULL) { return (0); } for (k = 0; z_zlist_get_zonename(zlst, k) != (char *)NULL; k++) { if (z_zlist_get_current_state(zlst, k) > ZONE_STATE_INSTALLED) { if (!z_zlist_change_zone_state(zlst, k, ZONE_STATE_INSTALLED)) { ret = -1; break; } } } /* Free zlst */ z_free_zone_list(zlst); } return (ret); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Module: zones_str.c * Group: libinstzones * Description: Private functions used by zones library functions to manipulate * strings * * Public Methods: * * _z_strAddToken - Add a token to a string * _z_strContainsToken - Does a given string contain a specified substring * _z_strGetToken - Get a separator delimited token from a string * _z_strGetToken_r - Get separator delimited token from string to fixed buffer * _z_strPrintf - Create string from printf style format and arguments * _z_strPrintf_r - Create string from printf style format and arguments * _z_strRemoveLeadingWhitespace - Remove leading whitespace from string * _z_strRemoveToken - Remove a token from a string */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ /* * Library Function Prototypes */ /* * Local Function Prototypes */ /* * Global internal (private) declarations */ /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: _z_strAddToken * Synopsis: Add a token to a string * Description: Append a token (sequence of one or more characters) to a * string that is in allocated space - create new string if * no string to append to exists * Arguments: a_old - [RO, *RW] - (char **) * - Pointer to handle to string to append token to * == NULL - new string is created * a_new - [RO, *RO] - (char *) * - Pointer to string representing token to append * to the end of the "a_old" string * == NULL - no action is performed * a_new[0] == '\0' - no action is performed * a_separator - [RO, *RO] - (char) * - One character placed between the old (existing) * string and the new token to be added IF the old * string exists and is not empty (zero length) * Returns: void * CAUTION: The old (existing) string must be allocated space (via lu_mem* * or _z_str* methods) - it must not be a static or inline * character string * NOTE: The old (existing) string may be freed with 'free' * if a token is appended to it * NOTE: Any string returned in 'a_old' is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the token string is no longer needed. */ void _z_strAddToken(char **a_old, char *a_new, char a_separator) { /* entry assertions */ assert(a_old != NULL); assert(a_separator != '\0'); /* if token to add is null or token is zero length, just return */ if (a_new == NULL || *a_new == '\0') { return; } /* make sure that new token does not contain the separator */ assert(strchr(a_new, (int)a_separator) == NULL); /* if old string is empty (zero length), deallocate */ if ((*a_old != NULL) && ((*a_old)[0] == '\0')) { /* *a_old is set to NULL by free */ free(*a_old); *a_old = NULL; } /* if old string exists, append separator and token */ if (*a_old != NULL) { char *p; p = _z_strPrintf("%s%c%s", *a_old, a_separator, a_new); free(*a_old); *a_old = p; return; } /* old string does not exist - return duplicate of token */ assert(*a_old == NULL); *a_old = _z_strdup(a_new); } /* * Name: _z_strContainsToken * Synopsis: Does a given string contain a specified substring * Description: Determine if a given substring exists in a larger string * Arguments: a_string - [RO, *RO] - (char *) * Pointer to string to look for substring in * a_token - [RO, *RO] - (char *) * Pointer to substring to look for in larger string * Results: boolean_t * B_TRUE - substring exists in larger string * B_FALSE - substring does NOT exist in larger string * NOTE: The substring must match on a "token" basis; that is, the * substring must exist in the larger string delineated with * either spaces or tabs to match. */ boolean_t _z_strContainsToken(char *a_string, char *a_token, char *a_separators) { char *lasts; char *current; char *p; /* entry assertions */ assert(a_separators != NULL); assert(*a_separators != '\0'); /* * if token is not supplied, no string provided, * or the string is an empty string, return false */ if (a_token == NULL || a_string == NULL || *a_string == '\0') { return (B_FALSE); } /* if no string provided, return false */ /* if string empty (zero length), return false */ /* duplicate larger string because strtok_r changes it */ p = _z_strdup(a_string); lasts = p; /* scan each token looking for a match */ while ((current = strtok_r(NULL, a_separators, &lasts)) != NULL) { if (strcmp(current, a_token) == 0) { free(p); return (B_TRUE); } } /* free up temporary storage */ free(p); /* not found */ return (B_FALSE); } /* * Name: _z_strGetToken * Synopsis: Get a separator delimited token from a string * Description: Given a string and a list of one or more separators, * return the position specified token (sequence of one or * more characters that do not include any of the separators) * Arguments: r_sep - [*RW] - (char *) * - separator that ended the token returned * - NOTE: this is a pointer to a "char", e.g.: * - char a; * - _z_strGetToken(&a, ...) * a_string - [RO, *RO] - (char *) * - pointer to string to extract token from * a_index - [RO, *RO] - (int) * - Index of token to return; '0' is first matching * token, '1' is second matching token, etc. * a_separators - [RO, *RO] - (char *) * - String containing one or more characters that * can separate one "token" from another * Returns: char * * == NULL - no token matching criteria found * != NULL - token matching criteria * NOTE: Any token string returned is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the token string is no longer needed. */ char * _z_strGetToken(char *r_sep, char *a_string, int a_index, char *a_separators) { char *p; char *q; char *lasts; /* entry assertions */ assert(a_string != NULL); assert(a_index >= 0); assert(a_separators != NULL); assert(*a_separators != '\0'); /* if returned separator requested, reset to null until token found */ if (r_sep != NULL) { *r_sep = '\0'; } /* duplicate original string before breaking down into tokens */ p = _z_strdup(a_string); lasts = p; /* scan for separators and return 'index'th token found */ while (q = strtok_r(NULL, a_separators, &lasts)) { /* retrieve separator if requested */ if (r_sep != NULL) { char *x; x = strpbrk(a_string, a_separators); if (x != NULL) { *r_sep = *x; } } /* if this is the 'index'th token requested return it */ if (a_index-- == 0) { char *tmp; /* duplicate token into its own storage */ tmp = _z_strdup(q); /* free up copy of original input string */ free(p); /* return token found */ return (tmp); } } /* * token not found */ /* free up copy of original input string */ free(p); /* return NULL pointer (token not found) */ return (NULL); } /* * Name: _z_strGetToken_r * Synopsis: Get separator delimited token from a string into a fixed buffer * Description: Given a string and a list of one or more separators, * return the position specified token (sequence of one or * more characters that do not include any of the separators) * into a specified buffer of a fixed maximum size * Arguments: r_sep - [*RW] - (char *) * - separator that ended the token returned * - NOTE: this is a pointer to a "char", e.g.: * - char a; * - _z_strGetToken(&a, ...) * a_string - [RO, *RO] - (char *) * - pointer to string to extract token from * a_index - [RO, *RO] - (int) * - Index of token to return; '0' is first matching * token, '1' is second matching token, etc. * a_separators - [RO, *RO] - (char *) * - String containing one or more characters that * can separate one "token" from another * a_buf - [RO, *RW] - (char *) * - Pointer to buffer used as storage space for the * returned token - the returned token is always * null terminated * a_buf[0] == '\0' - no token meeting criteria found * a_buf[0] != '\0' - token meeting criteria returned * a_bufLen - [RO, *RO] - (int) * - Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1' * bytes will be placed in 'a_buf' - the returned * token is always null terminated * Returns: void */ void _z_strGetToken_r(char *r_sep, char *a_string, int a_index, char *a_separators, char *a_buf, int a_bufLen) { char *p; char *q; char *lasts; /* entry assertions */ assert(a_string != NULL); assert(a_index >= 0); assert(a_separators != NULL); assert(*a_separators != '\0'); assert(a_buf != NULL); assert(a_bufLen > 0); /* reset returned separator */ if (r_sep != NULL) { *r_sep = '\0'; } /* zero out contents of return buffer */ bzero(a_buf, a_bufLen); /* duplicate original string before breaking down into tokens */ p = _z_strdup(a_string); lasts = p; /* scan for separators and return 'index'th token found */ while (q = strtok_r(NULL, a_separators, &lasts)) { /* retrieve separator if requested */ if (r_sep != NULL) { char *x; x = strpbrk(a_string, a_separators); if (x != NULL) { *r_sep = *x; } } /* if this is the 'index'th token requested return it */ if (a_index-- == 0) { /* copy as many characters as possible to return buf */ (void) strncpy(a_buf, q, a_bufLen-1); break; } } /* free up copy of original input string */ free(p); } /* * Name: _z_strPrintf * Synopsis: Create string from printf style format and arguments * Description: Call to convert a printf style format and arguments into a * string of characters placed in allocated storage * Arguments: format - [RO, RO*] (char *) * printf-style format for string to be formatted * VARG_LIST - [RO] (?) * arguments as appropriate to 'format' specified * Returns: char * * A string representing the printf conversion results * NOTE: Any string returned is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the string is no longer needed. * Errors: If the string cannot be created, the process exits */ /*PRINTFLIKE1*/ char * _z_strPrintf(char *a_format, ...) { va_list ap; size_t vres = 0; char bfr[1]; char *rstr = NULL; /* entry assertions */ assert(a_format != NULL); assert(*a_format != '\0'); /* determine size of the message in bytes */ va_start(ap, a_format); vres = vsnprintf(bfr, 1, a_format, ap); va_end(ap); assert(vres > 0); assert(vres < LINE_MAX); /* allocate storage to hold the message */ rstr = (char *)_z_calloc(vres+2); /* generate the results of the printf conversion */ va_start(ap, a_format); vres = vsnprintf(rstr, vres+1, a_format, ap); va_end(ap); assert(vres > 0); assert(vres < LINE_MAX); assert(*rstr != '\0'); /* return the results */ return (rstr); } /* * Name: _z_strPrintf_r * Synopsis: Create string from printf style format and arguments * Description: Call to convert a printf style format and arguments into a * string of characters placed in allocated storage * Arguments: a_buf - [RO, *RW] - (char *) * - Pointer to buffer used as storage space for the * returned string created * a_bufLen - [RO, *RO] - (int) * - Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1' * bytes will be placed in 'a_buf' - the returned * string is always null terminated * a_format - [RO, RO*] (char *) * printf-style format for string to be formatted * VARG_LIST - [RO] (?) * arguments as appropriate to 'format' specified * Returns: void */ /*PRINTFLIKE3*/ void _z_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...) { va_list ap; size_t vres = 0; /* entry assertions */ assert(a_format != NULL); assert(*a_format != '\0'); assert(a_buf != NULL); assert(a_bufLen > 1); /* generate the results of the printf conversion */ va_start(ap, a_format); vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap); va_end(ap); assert(vres > 0); assert(vres < a_bufLen); a_buf[a_bufLen-1] = '\0'; } /* * Name: _z_strRemoveLeadingWhitespace * Synopsis: Remove leading whitespace from string * Description: Remove all leading whitespace characters from a string * Arguments: a_str - [RO, *RW] - (char **) * Pointer to handle to string (in allocated storage) to * remove all leading whitespace from * Returns: void * The input string is modified as follows: * == NULL: * - input string was NULL * - input string is all whitespace * != NULL: * - copy of input string with leading * whitespace removed * CAUTION: The input string must be allocated space (via mem* or * _z_str* methods) - it must not be a static or inline * character string * NOTE: The input string a_str will be freed with 'free' * if it is all whitespace, or if it contains any leading * whitespace characters * NOTE: Any string returned is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the string is no longer needed. * Errors: If the string cannot be created, the process exits */ void _z_strRemoveLeadingWhitespace(char **a_str) { char *o_str; /* entry assertions */ assert(a_str != NULL); /* if string is null, just return */ if (*a_str == NULL) { return; } o_str = *a_str; /* if string is empty, deallocate and return NULL */ if (*o_str == '\0') { /* free string - handle is not reset to NULL by free */ free(*a_str); *a_str = NULL; return; } /* if first character is not a space, just return */ if (!isspace(*o_str)) { return; } /* advance past all space characters */ while ((*o_str != '\0') && (isspace(*o_str))) { o_str++; } /* if string was all space characters, deallocate and return NULL */ if (*o_str == '\0') { /* free string - *a_str is not reset to NULL by free */ free(*a_str); *a_str = NULL; return; } /* have non-space/null byte, return dup, deallocate original */ free(*a_str); *a_str = _z_strdup(o_str); } /* * Name: _z_strRemoveToken * Synopsis: Remove a token from a string * Description: Remove a token (sequence of one or more characters) from a * string that is in allocated space * Arguments: r_string - [RO, *RW] - (char **) * - Pointer to handle to string to remove token from * a_token - [RO, *RO] - (char *) * Pointer to token (substring) to look for and remove * from r_string provided * a_separators - [RO, *RO] - (char *) * - String containing one or more characters that * separate one "token" from another in r_string * a_index - [RO, *RO] - (int) * - Index of token to remove; '0' is first matching * token, '1' is second matching token, etc. * Returns: void * CAUTION: The input string must be allocated space (via lu_mem* or * _z_str* methods) - it must not be a static or inline * character string * NOTE: The input string r_string will be freed with 'free' * if the token to be removed is found * NOTE: Any token string returned is placed in new storage for the * calling method. The caller must use 'free' to dispose * of the storage once the token string is no longer needed. * Errors: If the new token string cannot be created, the process exits */ void _z_strRemoveToken(char **r_string, char *a_token, char *a_separators, int a_index) { char *a_string; char *copyString; char sep = 0; int copyLength; int i; /* entry assertions */ assert(r_string != NULL); assert(a_token != NULL); assert(*a_token != '\0'); assert(a_separators != NULL); assert(*a_separators != '\0'); /* simple case: input string is null; return empty string */ a_string = *r_string; if (*a_string == '\0') { return; } /* simple case: token == input string; return empty string */ if (strcmp(a_string, a_token) == 0) { /* * deallocate input string; free doesn't * set *r_string to NULL */ free(*r_string); *r_string = NULL; return; } /* simple case: token not in input string: return */ if (!_z_strContainsToken(a_string, a_token, a_separators)) { return; } /* * Pick apart the old string building the new one as we go along * removing the first occurance of the token provided */ copyLength = (strlen(a_string)-strlen(a_token))+2; copyString = (char *)_z_calloc(copyLength); for (i = 0; ; i++) { char *p; p = _z_strGetToken(&sep, a_string, i, a_separators); if (p == NULL) { break; } if ((strcmp(p, a_token) == 0) && (a_index-- == 0)) { free(p); continue; } if (*copyString) { assert(sep != '\0'); (void) strncat(copyString, &sep, 1); } (void) strcat(copyString, p); free(p); } free(*r_string); assert(*copyString); *r_string = copyString; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _ZONES_STRINGS_H #define _ZONES_STRINGS_H /* * Module: zones_strings.h * Group: libinstzones * Description: This header contains strings used in libinstzones * library modules. */ #include /* * C++ prefix */ #ifdef __cplusplus extern "C" { #endif /* constants */ #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "SUNW_INSTALL_LIBZONES" #endif #ifndef ILIBSTR #define ILIBSTR(x) dgettext(TEXT_DOMAIN, x) #endif /* * message strings */ /* BEGIN CSTYLED */ /* * I18N: these messages are debugging message and are only displayed * when special debugging output has been enabled - these messages * will never be displayed during normal product usage */ #define DBG_ARG ILIBSTR("argument <%d> = <%s>") #define DBG_LIBRARY_NOT_FOUND ILIBSTR("unable to dlopen library <%s>: %s") #define DBG_MNTPT_NAMES ILIBSTR("mount point for global zone path <%s> in zone <%s> is global zone mount point <%s> non-global zone mount point <%s>") #define DBG_TO_ZONEHALT ILIBSTR("halting zone <%s>") #define DBG_TO_ZONEREADY ILIBSTR("readying zone <%s>") #define DBG_TO_ZONERUNNING ILIBSTR("running zone <%s>") #define DBG_TO_ZONEUNMOUNT ILIBSTR("unmounting zone <%s>") #define DBG_UNMOUNTING_DEV ILIBSTR("unmounting package device <%s>") #define DBG_ZONES_ADJLCKOBJ_EXIT ILIBSTR("lock object <%s> adjusted to <%s> for root path <%s> resolved <%s>") #define DBG_ZONES_APLK ILIBSTR("acquire lock zone <%s> lock <%s> pid <%ld>") #define DBG_ZONES_APLK_EXIT ILIBSTR("acquire lock failure zone <%s> lock <%s> pid <%ld>: return <%d> status <%d> <%s>") #define DBG_ZONES_APLK_RESULTS ILIBSTR("acquire lock success zone <%s> lock <%s> key <%s> results <%s>") #define DBG_ZONES_ARE_IMPLEMENTED ILIBSTR("zones are implemented") #define DBG_ZONES_CHG_Z_STATE ILIBSTR("change zone <%s> from state <%d> to state <%d>") #define DBG_ZONES_CHG_Z_STATE_ENTRY ILIBSTR("change zone <%d> to state <%d>") #define DBG_ZONES_GET_ZONE_STATE ILIBSTR("state of zone <%s> is <%ld>") #define DBG_ZONES_LCK_OBJ ILIBSTR("lock zone object <%s> zone <%s> pid <%ld> locks <%s>") #define DBG_ZONES_LCK_OBJ_FOUND ILIBSTR("lock zone examining object <%s> key <%s>: match") #define DBG_ZONES_LCK_OBJ_NOTFOUND ILIBSTR("lock zone examining object <%s> key <%s>: NO MATCH") #define DBG_ZONES_LCK_OBJ_NOTHELD ILIBSTR("object <%s> not locked on zone <%s>") #define DBG_ZONES_LCK_THIS ILIBSTR("lock this zone flags <0x%08lx>") #define DBG_ZONES_LCK_ZONE ILIBSTR("lock zone <%s> flags <0x%08lx>") #define DBG_ZONES_LCK_ZONES ILIBSTR("lock zones flags <0x%08lx>") #define DBG_ZONES_LCK_ZONES_EXIST ILIBSTR("locking all non-global zones defined") #define DBG_ZONES_LCK_ZONES_NOZONES ILIBSTR("no zones locked: no non-global zones exist") #define DBG_ZONES_LCK_ZONES_UNIMP ILIBSTR("no zones locked: zones are not implemented") #define DBG_ZONES_LCK_ZONE_PKGADM ILIBSTR("locking package administration: zone <%s> object <%s>") #define DBG_ZONES_LCK_ZONE_ZONEADM ILIBSTR("locking zone administration: zone <%s> object <%s>") #define DBG_ZONES_MOUNT_IN_LZ_ENTRY ILIBSTR("mount in non-global zone: zone <%s> global-zone path <%s>") #define DBG_ZONES_NGZ_LIST_STATES ILIBSTR("non-global zone <%s> current state <%d> kernel status <%d>") #define DBG_ZONES_NOT_IMPLEMENTED ILIBSTR("zones are NOT implemented") #define DBG_ZONES_RELK ILIBSTR("release lock zone <%s> lock <%s> key <%s>") #define DBG_ZONES_RELK_EXIT ILIBSTR("release lock <%s> key <%s> to zone <%s>: return <%d> status <%d> results <%s>") #define DBG_ZONES_ULK_OBJ ILIBSTR("unlock zone object <%s> zone <%s> locks <%s>") #define DBG_ZONES_ULK_OBJ_FOUND ILIBSTR("unlock zone examining object <%s> key <%s>: match") #define DBG_ZONES_ULK_OBJ_NONE ILIBSTR("no objects locked on zone <%s>") #define DBG_ZONES_ULK_OBJ_NOTFOUND ILIBSTR("unlock zone examining object <%s> key <%s>: NO MATCH") #define DBG_ZONES_ULK_OBJ_NOTHELD ILIBSTR("object <%s> not locked on zone <%s>") #define DBG_ZONES_ULK_THIS ILIBSTR("unlock this zone flags <0x%08lx>") #define DBG_ZONES_ULK_ZONE ILIBSTR("unlock zone <%s> flags <0x%08lx>") #define DBG_ZONES_ULK_ZONES ILIBSTR("unlock zones flags <0x%08lx>") #define DBG_ZONES_ULK_ZONES_EXIST ILIBSTR("unlocking all non-global zones defined") #define DBG_ZONES_ULK_ZONES_NOZONES ILIBSTR("no zones unlocked: no non-global zones exist") #define DBG_ZONES_ULK_ZONES_UNIMP ILIBSTR("no zones unlocked: zones are not implemented") #define DBG_ZONES_ULK_ZONE_PKGADM ILIBSTR("unlocking package administration: zone <%s> object <%s>") #define DBG_ZONES_ULK_ZONE_ZONEADM ILIBSTR("unlocking zone administration: zone <%s> object <%s>") #define DBG_ZONES_UNMOUNT_FROM_LZ_ENTRY ILIBSTR("unmount non-global zone: mount point <%s>") #define DBG_ZONE_EXEC_CMD_ENTER ILIBSTR("execute command <%s> on zone <%s> this zone <%s>") #define DBG_BRANDS_ARE_IMPLEMENTED ILIBSTR("brands are implemented") #define DBG_BRANDS_NOT_IMPLEMENTED ILIBSTR("brands are NOT implemented") /* * I18N: these messages are error messages that can be displayed * during the normal usage of the products */ #define ERR_CANNOT_CREATE_CONTRACT ILIBSTR("unable to create contract: %s") #define ERR_CAPTURE_FILE ILIBSTR("unable to open command output capture file <%s>: %s") #define ERR_FORK ILIBSTR("unable to create new process: %s") #define ERR_GET_ZONEID ILIBSTR("unable to get id of zone <%s>: %s") #define ERR_GZMOUNT_FAILED ILIBSTR("unable to mount global path <%s> local path <%s> zone <%s>: %s") #define ERR_GZMOUNT_RESOLVEPATH ILIBSTR("unable to determine zone <%s> dev path from <%s>: %s") #define ERR_GZMOUNT_SNPRINTFGMP_FAILED ILIBSTR("unable to create global zone mount point <%s> from <%s> <%s> <%s>: combined path exceeds maximum length of <%ld>") #define ERR_GZMOUNT_SNPRINTFLMP_FAILED ILIBSTR("unable to create local zone mount point <%s> from <%s>: combined path exceeds maximum length of <%ld>") #define ERR_GZMOUNT_SNPRINTFUUID_FAILED ILIBSTR("unable to create uuid <%s>: combined uuid exceeds maximum length of <%ld>") #define ERR_GZMOUNT_SNPRINTF_FAILED ILIBSTR("unable to create path <%s> from <%s>: combined path exceeds maximum length of <%ld>") #define ERR_GZPATH_NOT_ABSOLUTE ILIBSTR("unable to mount global zone path <%s>: path must be absolute") #define ERR_GZPATH_NOT_DIR ILIBSTR("unable to mount global zone path <%s>: %s") #define ERR_GZUMOUNT_FAILED ILIBSTR("unable to unmount <%s>: %s") #define ERR_LZMNTPT_NOTDIR ILIBSTR("unable to unmount global zone mount point <%s>: %s") #define ERR_LZMNTPT_NOT_ABSOLUTE ILIBSTR("unable to unmount <%s>: path must be absolute") #define ERR_LZROOT_NOTDIR ILIBSTR("unable to use <%s> as zone root path: %s") #define ERR_MALLOC ILIBSTR("unable to allocate %s memory, errno %d: %s") #define ERR_MEM ILIBSTR("unable to allocate memory.") #define ERR_MEMORY ILIBSTR("memory allocation failure, errno=%d") #define ERR_MNTPT_MKDIR ILIBSTR("unable to create temporary mount point <%s> in zone <%s>: %s") #define ERR_NO_ZONE_ROOTPATH ILIBSTR("unable to get root path of zone <%s>: %s") #define ERR_ROOTPATH_EMPTY ILIBSTR("unable to get root path of zone <%s>: empty path returned") #define ERR_ZEXEC_ASSEMBLE ILIBSTR("unable to establish connection with zone <%s>: could not assemble new environment") #define ERR_ZEXEC_BADSTATE ILIBSTR("unable to establish connection with zone <%s>: zone is in state '%s'") #define ERR_ZEXEC_BADZONE ILIBSTR("unable to establish connection with zone <%s>: no such zone") #define ERR_ZEXEC_EFAULT ILIBSTR("one or more file descriptors may be non-local (such as open across nfs): %s") #define ERR_ZEXEC_EXECFAILURE ILIBSTR("unable to establish connection with zone <%s>: exec failure: %s") #define ERR_ZEXEC_GETPPRIV ILIBSTR("unable to establish connection with zone <%s>: getppriv failed: %s") #define ERR_ZEXEC_GZUSED ILIBSTR("unable to establish connection with zone <%s>: global zone specified") #define ERR_ZEXEC_NOROOTPATH ILIBSTR("unable to establish connection with zone <%s>: cannot get root path: %s") #define ERR_ZEXEC_NOTRUNNING ILIBSTR("unable to establish connection with zone <%s>: not running - in state '%s'") #define ERR_ZEXEC_NOT_IN_GZ ILIBSTR("unable to establish connection with zone <%s>: not in the global zone") #define ERR_ZEXEC_NOZONEID ILIBSTR("unable to establish connection with zone <%s>: cannot get zone id: %s") #define ERR_ZEXEC_PRIVS ILIBSTR("unable to establish connection with zone <%s>: you lack sufficient privilege to access the zone") #define ERR_ZEXEC_PRIV_ALLOCSET ILIBSTR("unable to establish connection with zone <%s>o: priv_allocset failed: %s") #define ERR_ZEXEC_ZONEENTER ILIBSTR("unable to establish connection with zone <%s>: could not enter zone: %s") #define ERR_ZONEBOOT_CMD_ERROR ILIBSTR("unable to boot zone: problem running <%s> on zone <%s>: error %d%s%s") #define ERR_ZONEBOOT_CMD_SIGNAL ILIBSTR("unable to boot zone: problem running <%s> on zone <%s>: terminated by signal") #define ERR_ZONEBOOT_DIDNT_BOOT ILIBSTR("unable to boot zone <%s>: zone failed to transition to running state") #define ERR_ZONEBOOT_EXEC ILIBSTR("unable to boot zone: could not execute zone administration command <%s>: %s") #define ERR_ZONEHALT_EXEC ILIBSTR("unable to halt zone: could not execute zone administration command <%s>: %s") #define ERR_ZONEINDEX_OPEN ILIBSTR("unable to open zone index file %s: %s") #define ERR_ZONEREADY_CMDFAIL ILIBSTR("unable to ready zone: problem running <%s> on zone <%s>: %s%s%s") #define ERR_ZONEREADY_DIDNT_READY ILIBSTR("unable to ready zone <%s>: zone failed to transition to ready state") #define ERR_ZONEREADY_EXEC ILIBSTR("unable to ready zone: could not execute zone administration command <%s>: %s") #define ERR_ZONEROOT_NOTDIR ILIBSTR("unable to use temporary mount point <%s> in zone <%s>: %s") #define ERR_ZONES_LCK_THIS_PKGADM ILIBSTR("Unable to acquire package administration lock for this system; try again later") #define ERR_ZONES_LCK_THIS_ZONEADM ILIBSTR("Unable to acquire zone administration lock for this system; please try again later") #define ERR_ZONES_LCK_ZONES_FAILED ILIBSTR("Unable to acquire lock on non-global zone <%s>: releasing all locks") #define ERR_ZONES_LCK_ZONE_PKGADM ILIBSTR("Unable to acquire package administration lock for zone <%s>; please try again later") #define ERR_ZONES_LCK_ZONE_ZONEADM ILIBSTR("Unable to acquire zone administration lock for zone <%s>; please try again later") #define ERR_ZONES_NOT_IMPLEMENTED ILIBSTR("error: zones not implemented") #define ERR_ZONES_ULK_THIS_PACKAGE ILIBSTR("Unable to release package administration lock for this system; try again later") #define ERR_ZONES_ULK_THIS_ZONES ILIBSTR("Unable to release zone administration lock for this system; please try again later") #define ERR_ZONE_LIST_EMPTY ILIBSTR("empty zone list specified") #define ERR_ZONE_NAME_ILLEGAL ILIBSTR("illegal zone name %.*s") #define ERR_ZONE_NONEXISTENT ILIBSTR("zone %s does not exist") #define ERR_OPEN_READ ILIBSTR("unable to open <%s> for reading: (%d) %s") #define ERR_ZONEUNMOUNT_CMD_SIGNAL ILIBSTR("unable to unmount zone: problem running <%s> on zone <%s>: terminated by signal") #define ERR_ZONEUNMOUNT_EXEC ILIBSTR("unable to unmount zone: could not execute zone administration command <%s>: %s") #define ERR_ZONEUNMOUNT_CMD_ERROR ILIBSTR("unable to unmount zone: problem running <%s> on zone <%s>: error %d%s%s") #define ERR_BRAND_GETBRAND ILIBSTR("unable to get zone brand: zonecfg_get_brand: %s") /* * I18N: these are messages that can be displayed during the normal * usage of the products */ #define MSG_PROG_ERR ILIBSTR("ERROR: %s") #define MSG_ZONES_LCK_THIS_PKGADM ILIBSTR("## Waiting for up to <%ld> seconds for package administration commands to become available (another user is administering packages)") #define MSG_ZONES_LCK_THIS_ZONEADM ILIBSTR("## Waiting for up to <%ld> seconds for zone administration commands to become available (another user is administering zones)") #define MSG_ZONES_LCK_ZONE_PKGADM ILIBSTR("## Waiting for up to <%ld> seconds for package administration commands to become available (another user is administering packages on zone <%s>)") #define MSG_ZONES_LCK_ZONE_ZONEADM ILIBSTR("## Waiting for up to <%ld> seconds for zone administration commands to become available (another user is administering zones on zone <%s>)") /* * I18N: these messages are warning messages that can be displayed * during the normal usage of the products */ #define WRN_ZONES_ULK_ZONE_PKGADM ILIBSTR("WARNING: Unable to release package administration lock for zone <%s>") #define WRN_ZONES_ULK_ZONE_ZONEADM ILIBSTR("WARNING: Unable to release zone administration lock for zone <%s>") /* END CSTYLED */ /* * C++ postfix */ #ifdef __cplusplus } #endif #endif /* _ZONES_STRINGS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Module: zones.c * Group: libinstzones * Description: Provide "zones" interface for install consolidation code * * Public Methods: * * _z_close_file_descriptors - close a file descriptor "a_fd" not in the * list "a_fds" * _z_echo - Output an interactive message if interaction is enabled * _z_echoDebug - Output a debugging message if debugging is enabled * _z_is_directory - determine if specified path exists and is a directory * _z_program_error - Output an error message to the appropriate destinations * _z_pluginCatchSigint - SIGINT/SIGHUP interrupt handler * _z_running_in_global_zone - Determine if this process is running in the * global zone * _z_zones_are_implemented - Determine if zones are supported by the * current system * _z_brands_are_implemented - determine if branded zones are implemented on * this system */ /* * System includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * local includes */ #include "instzones_lib.h" #include "zones_strings.h" /* * Private structures */ /* * these dynamic libraries are required in order to use the branded zones * functionality. If these libraries are not available at runtime, * then the zones we find are assumed to be native zones. */ #define BRAND1_LIBRARY "libbrand.so.1" #define BRAND_LIBRARY "libbrand.so" /* * Library Function Prototypes */ /* * Local Function Prototypes */ static void error_and_exit(int error_num); static void (*fatal_err_func)() = &error_and_exit; /* ----------------------- private functions -------------------------- */ /* * error_and_exit() * Abort routine. An exit code of '2' is used by all applications * to indicate a non-recoverable fatal error. * Parameters: * error_num - error index number: * ERR_MALLOC_FAIL * Return: * none * Status: * private */ static void error_and_exit(int error_num) { if (error_num == ERR_MALLOC_FAIL) (void) fprintf(stderr, "Allocation of memory failed\n"); else (void) fprintf(stderr, "ERROR: code %d\n", error_num); exit(2); } /* * ***************************************************************************** * global external (public) functions * ***************************************************************************** */ /* * Name: _z_close_file_descriptors * Description: close a file descriptor "a_fd" not in the list "a_fds" * This function is called from the fdwalk() library function. * If the file descriptor passed in is NOT in the list passed in, * the file is closed. * Arguments: a_fds - [RO, *RO] - (void *) * Pointer to list of file descriptors to keep open * a_fd - [RO, *RO] - (int) * File descriptor to check * Returns: int * 0 - success */ int _z_close_file_descriptors(void *a_fds, int a_fd) { int *fds; int i; /* do not close standard input, output, or error file descriptors */ if (a_fd == STDIN_FILENO || a_fd == STDOUT_FILENO || a_fd == STDERR_FILENO) { return (0); } /* if no file descriptor retention list, close this file */ if (a_fds == (void *)NULL) { (void) close(a_fd); return (0); } /* * retention list provided, skip this descriptor if its in the list */ fds = (int *)a_fds; for (i = 0; fds[i] != -1; i++) { if (fds[i] == a_fd) { return (0); } } /* this descriptor not in retention list - close this file */ (void) close(a_fd); return (0); } /* * Name: _z_echo * Synopsis: Output an interactive message if interaction is enabled * Description: Main method for outputting an interactive message; call to * output interactive message if interation has not been disabled * by a previous call to echoSetFlag(0). * Arguments: format - [RO, RO*] (char *) * printf-style format for debugging message to be output * VARG_LIST - [RO] (?) * arguments as appropriate to 'format' specified * Returns: void */ /*PRINTFLIKE1*/ void _z_echo(char *a_format, ...) { va_list ap; char message[MAX_MESSAGE_SIZE]; /* entry assertions */ assert(a_format != NULL); /* return if no progerr function registered */ if (_z_global_data._z_echo == NULL) { return; } /* capture message */ va_start(ap, a_format); (void) vsnprintf(message, sizeof (message), a_format, ap); va_end(ap); /* pass message to registered function */ (_z_global_data._z_echo)("%s", message); } /* * Name: _z_echoDebug * Synopsis: Output a debugging message if debugging is enabled * Description: Main method for outputting a debugging message; call to * output debugging message if debugging has been enabled * by a previous call to _z_echoDebugSetFlag(1). * Arguments: format - [RO, RO*] (char *) * printf-style format for debugging message to be output * VARG_LIST - [RO] (?) * arguments as appropriate to 'format' specified * Returns: void * NOTE: format of message will be: * # [ aaa bbb ccc ] message * where: aaa - process i.d. * bbb - zone i.d. * ccc - name of program * for example: * # [ 25685 0 pkgadd ] unable to get package list */ /*PRINTFLIKE1*/ void _z_echoDebug(char *a_format, ...) { va_list ap; char message[MAX_MESSAGE_SIZE]; /* entry assertions */ assert(a_format != NULL); /* return if no progerr function registered */ if (_z_global_data._z_echo_debug == NULL) { return; } /* capture message */ va_start(ap, a_format); (void) vsnprintf(message, sizeof (message), a_format, ap); va_end(ap); /* pass message to registered function */ (_z_global_data._z_echo_debug)("%s", message); } /* * Name: _z_is_directory * Description: determine if specified path exists and is a directory * Arguments: path - pointer to string representing the path to verify * returns: 0 - directory exists * 1 - directory does not exist or is not a directory * NOTE: errno is set appropriately */ int _z_is_directory(char *path) { struct stat statbuf; /* entry assertions */ assert(path != NULL); assert(*path != '\0'); /* return error if path does not exist */ if (stat(path, &statbuf) != 0) { return (1); } /* return error if path is not a directory */ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { errno = ENOTDIR; return (1); } /* path exists and is a directory */ return (0); } /* * Name: _z_pluginCatchSigint * Synopsis: SIGINT/SIGHUP interrupt handler * Description: Catch the "SIGINT" and "SIGHUP" signals: * -> increment _z_SigReceived global variable * -> propagate signal to "_z_ChildProcessId" if registered (!= -1) * Arguments: signo - [RO, *RO] - (int) * Signal number that was caught * Returns: void */ void _z_sig_trap(int a_signo) { /* bump signals received count */ _z_global_data._z_SigReceived++; /* if child process registered, propagate signal to child */ if (_z_global_data._z_ChildProcessId > 0) { (void) kill(_z_global_data._z_ChildProcessId, a_signo); } } /* * Name: _z_program_error * Description: Output an error message to the appropriate destinations * Arguments: format - [RO, RO*] (char *) * printf-style format for debugging message to be output * VARG_LIST - [RO] (?) * arguments as appropriate to 'format' specified * Returns: void * NOTE: format of message will be: * [aaa: ] ERROR: message * where: aaa - program name (if set) * message - results of format and arguments * for example: * ERROR: unable to get package list */ /*PRINTFLIKE1*/ void _z_program_error(char *a_format, ...) { va_list ap; char message[MAX_MESSAGE_SIZE]; /* entry assertions */ assert(a_format != NULL); /* return if no progerr function registered */ if (_z_global_data._z_progerr == NULL) { return; } /* capture message */ va_start(ap, a_format); (void) vsnprintf(message, sizeof (message), a_format, ap); va_end(ap); /* pass message to registered function */ (_z_global_data._z_progerr)(MSG_PROG_ERR, message); } /* * Name: _z_running_in_global_zone * Synopsis: Determine if this process is running in the global zone * Arguments: void * Returns: boolean_t * == B_TRUE - this process is running in the global zone * == B_FALSE - this process is running in a nonglobal zone */ boolean_t _z_running_in_global_zone(void) { zoneid_t zoneid = (zoneid_t)-1; /* * if zones are not implemented, there is no way to tell if zones * are supported or not - in this case, we can only be running in the * global zone (since non-global zones cannot exist) so return TRUE */ if (z_zones_are_implemented() == B_FALSE) { return (B_TRUE); } /* get the zone i.d. of the current zone */ zoneid = getzoneid(); /* return TRUE if this is the global zone i.d. */ if (zoneid == GLOBAL_ZONEID) { return (B_TRUE); } /* return FALSE - not in the global zone */ return (B_FALSE); } /* * Name: _z_zones_are_implemented * Synopsis: Determine if zones are supported by the current system * Arguments: void * Returns: boolean_t * == B_TRUE - zones are supported * == B_FALSE - zones are not supported */ boolean_t _z_zones_are_implemented(void) { void *libptr = NULL; /* locate zone cfg library */ libptr = dlopen(ZONECFG_LIBRARY, RTLD_NOW|RTLD_GLOBAL); if (libptr == (void *)NULL) { _z_echoDebug(DBG_LIBRARY_NOT_FOUND, ZONECFG_LIBRARY, dlerror()); libptr = dlopen(ZONECFG1_LIBRARY, RTLD_NOW|RTLD_GLOBAL); } /* return false if library not available */ if (libptr == (void *)NULL) { _z_echoDebug(DBG_LIBRARY_NOT_FOUND, ZONECFG1_LIBRARY, dlerror()); return (B_FALSE); } /* library available - close handle */ (void) dlclose(libptr); /* locate contract filesystem library */ libptr = dlopen(CONTRACT_LIBRARY, RTLD_NOW|RTLD_GLOBAL); if (libptr == (void *)NULL) { _z_echoDebug(DBG_LIBRARY_NOT_FOUND, CONTRACT_LIBRARY, dlerror()); libptr = dlopen(CONTRACT1_LIBRARY, RTLD_NOW|RTLD_GLOBAL); } /* return false if library not available */ if (libptr == (void *)NULL) { _z_echoDebug(DBG_LIBRARY_NOT_FOUND, CONTRACT1_LIBRARY, dlerror()); return (B_FALSE); } /* library available - close handle */ (void) dlclose(libptr); /* return success */ return (B_TRUE); } boolean_t _z_brands_are_implemented(void) { void *libptr; /* locate brand library */ libptr = dlopen(BRAND_LIBRARY, RTLD_NOW|RTLD_GLOBAL); if (libptr == NULL) { _z_echoDebug(DBG_LIBRARY_NOT_FOUND, BRAND_LIBRARY, dlerror()); libptr = dlopen(BRAND1_LIBRARY, RTLD_NOW|RTLD_GLOBAL); } /* return false if library not available */ if (libptr == NULL) { _z_echoDebug(DBG_LIBRARY_NOT_FOUND, BRAND1_LIBRARY, dlerror()); return (B_FALSE); } /* library available - close handle */ (void) dlclose(libptr); /* return success */ return (B_TRUE); } /* * z_calloc() * Allocate 'size' bytes from the heap using calloc() * Parameters: * size - number of bytes to allocate * Return: * NULL - calloc() failure * void * - pointer to allocated structure * Status: * public */ void * _z_calloc(size_t size) { void * tmp; if ((tmp = (void *) malloc(size)) == NULL) { fatal_err_func(ERR_MALLOC_FAIL); return (NULL); } (void) memset(tmp, 0, size); return (tmp); } /* * z_malloc() * Alloc 'size' bytes from heap using malloc() * Parameters: * size - number of bytes to malloc * Return: * NULL - malloc() failure * void * - pointer to allocated structure * Status: * public */ void * _z_malloc(size_t size) { void *tmp; if ((tmp = (void *) malloc(size)) == NULL) { fatal_err_func(ERR_MALLOC_FAIL); return (NULL); } else return (tmp); } /* * _z_realloc() * Calls realloc() with the specfied parameters. _z_realloc() * checks for realloc failures and adjusts the return value * automatically. * Parameters: * ptr - pointer to existing data block * size - number of bytes additional * Return: * NULL - realloc() failed * void * - pointer to realloc'd structured * Status: * public */ void * _z_realloc(void *ptr, size_t size) { void *tmp; if ((tmp = (void *)realloc(ptr, size)) == (void *)NULL) { fatal_err_func(ERR_MALLOC_FAIL); return ((void *)NULL); } else return (tmp); } /* * z_strdup() * Allocate space for the string from the heap, copy 'str' into it, * and return a pointer to it. * Parameters: * str - string to duplicate * Return: * NULL - duplication failed or 'str' was NULL * char * - pointer to newly allocated/initialized structure * Status: * public */ void * _z_strdup(char *str) { char *tmp; if (str == NULL) return ((char *)NULL); if ((tmp = strdup(str)) == NULL) { fatal_err_func(ERR_MALLOC_FAIL); return ((char *)NULL); } else return (tmp); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _INSTZONES_API_H #define _INSTZONES_API_H /* * Module: instzones_api.h * Group: libinstzones * Description: This module contains the libinstzones API data structures, * constants, and function prototypes. */ /* * required includes */ /* System includes */ #include #include #include #include #include #include #include #include #include /* * C++ prefix */ #ifdef __cplusplus extern "C" { #endif /* function prototypes */ /* PRINTFLIKE1 */ typedef void (*_z_printf_fcn_t)(char *a_format, ...); /* zone list structure */ typedef struct _zoneListElement_t *zoneList_t; /* zone brand list structure */ typedef struct _zoneBrandList zoneBrandList_t; /* flag for zone locking functions */ typedef unsigned long ZLOCKS_T; /* flags for zone locking */ #define ZLOCKS_ZONE_ADMIN ((ZLOCKS_T)0x00000001) /* zone admin */ #define ZLOCKS_PKG_ADMIN ((ZLOCKS_T)0x00000002) /* package admin */ #define ZLOCKS_ALL ((ZLOCKS_T)0xFFFFFFFF) /* all locks */ #define ZLOCKS_NONE ((ZLOCKS_T)0x00000000) /* no locks */ /* * external function definitions */ /* zones.c */ extern boolean_t z_zones_are_implemented(void); extern void z_set_zone_root(const char *zroot); extern boolean_t z_zlist_is_zone_runnable(zoneList_t a_zoneList, int a_zoneIndex); extern boolean_t z_zlist_restore_zone_state(zoneList_t a_zoneList, int a_zoneIndex); extern boolean_t z_zlist_change_zone_state(zoneList_t a_zoneList, int a_zoneIndex, zone_state_t a_newState); extern char *z_get_zonename(void); extern zone_state_t z_zlist_get_current_state(zoneList_t a_zoneList, int a_zoneIndex); extern zone_state_t z_zlist_get_original_state(zoneList_t a_zoneList, int a_zoneIndex); extern int z_zoneExecCmdArray(int *r_status, char **r_results, char *a_inputFile, char *a_path, char *a_argv[], const char *a_zoneName, int *a_fds); extern int z_zone_exec(const char *zonename, const char *path, char *argv[], char *a_stdoutPath, char *a_stderrPath, int *a_fds); extern boolean_t z_create_zone_admin_file(char *a_zoneAdminFilename, char *a_userAdminFilename); extern void z_free_zone_list(zoneList_t a_zoneList); extern zoneList_t z_get_nonglobal_zone_list(void); extern zoneList_t z_get_nonglobal_zone_list_by_brand(zoneBrandList_t *); extern void z_free_brand_list(zoneBrandList_t *a_brandList); extern zoneBrandList_t *z_make_brand_list(const char *brandList, const char *delim); extern boolean_t z_lock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags); extern boolean_t z_non_global_zones_exist(void); extern boolean_t z_running_in_global_zone(void); extern void z_set_output_functions(_z_printf_fcn_t a_echo_fcn, _z_printf_fcn_t a_echo_debug_fcn, _z_printf_fcn_t a_progerr_fcn); extern int z_set_zone_spec(const char *zlist); extern int z_verify_zone_spec(void); extern boolean_t z_on_zone_spec(const char *zonename); extern boolean_t z_global_only(void); extern boolean_t z_unlock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags); extern boolean_t z_lock_this_zone(ZLOCKS_T a_lflags); extern boolean_t z_unlock_this_zone(ZLOCKS_T a_lflags); extern char *z_zlist_get_zonename(zoneList_t a_zoneList, int a_zoneId); extern char *z_zlist_get_zonepath(zoneList_t a_zoneList, int a_zoneId); extern char *z_zlist_get_scratch(zoneList_t a_zoneList, int a_zoneId); extern boolean_t z_umount_lz_mount(char *a_lzMountPoint); extern boolean_t z_mount_in_lz(char **r_lzMountPoint, char **r_lzRootPath, char *a_zoneName, char *a_gzPath, char *a_mountPointPrefix); extern boolean_t z_is_zone_branded(char *zoneName); extern boolean_t z_is_zone_brand_in_list(char *zoneName, zoneBrandList_t *brands); extern boolean_t z_zones_are_implemented(void); /* zones_exec.c */ extern int z_ExecCmdArray(int *r_status, char **r_results, char *a_inputFile, char *a_cmd, char **a_args); /*VARARGS*/ extern int z_ExecCmdList(int *r_status, char **r_results, char *a_inputFile, char *a_cmd, ...); /* zones_paths.c */ extern char *z_make_zone_root(char *); extern void z_path_canonize(char *file); extern void z_canoninplace(char *file); /* zones_lofs.c */ extern void z_destroyMountTable(void); extern int z_createMountTable(void); extern int z_isPathWritable(const char *); extern void z_resolve_lofs(char *path, size_t); /* zones_states.c */ extern int UmountAllZones(char *mntpnt); /* * C++ postfix */ #ifdef __cplusplus } #endif #endif /* _INSTZONES_API_H */