diff options
| author | Moriah Waterland <Moriah.Waterland@Sun.COM> | 2009-06-03 20:16:25 -0600 |
|---|---|---|
| committer | Moriah Waterland <Moriah.Waterland@Sun.COM> | 2009-06-03 20:16:25 -0600 |
| commit | 5c51f1241dbbdf2656d0e10011981411ed0c9673 (patch) | |
| tree | 0f30a2e38fe4e5d53a5a67264ba548577d82a86f /usr/src/lib/libinstzones | |
| parent | 2b79d384d32b4ea1e278466cd9b0f3bb56daae22 (diff) | |
| download | illumos-joyent-5c51f1241dbbdf2656d0e10011981411ed0c9673.tar.gz | |
6739234 move SVR4 packaging to ONNV gate
Diffstat (limited to 'usr/src/lib/libinstzones')
18 files changed, 8698 insertions, 0 deletions
diff --git a/usr/src/lib/libinstzones/Makefile b/usr/src/lib/libinstzones/Makefile new file mode 100644 index 0000000000..a00bec1cf5 --- /dev/null +++ b/usr/src/lib/libinstzones/Makefile @@ -0,0 +1,43 @@ +# +# 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. +# + +SUBDIRS = $(MACH) + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +_msg := TARGET = _msg +lint := TARGET = lint + + +.KEEP_STATE: + +all clean clobber install _msg lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/libinstzones/Makefile.com b/usr/src/lib/libinstzones/Makefile.com new file mode 100644 index 0000000000..994d0e5e91 --- /dev/null +++ b/usr/src/lib/libinstzones/Makefile.com @@ -0,0 +1,78 @@ +# +# 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. +# + +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) + +# openssl forces us to ignore dubious pointer casts, thanks to its clever +# use of macros for stack management. +LINTFLAGS= -umx -errtags \ + -erroff=E_BAD_PTR_CAST_ALIGN,E_BAD_PTR_CAST +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) + +LIBS = $(DYNLIB) $(LINTLIB) + +DYNFLAGS += $(ZLAZYLOAD) + +LDLIBS += -lc -lcontract -lzonecfg + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -I$(SRCDIR) + +.KEEP_STATE: + +all: $(LIBS) + +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) + +_msg: $(MSGDOMAINPOFILE) + +lint: lintcheck + +# include library targets +include $(SRC)/lib/Makefile.targ +include $(SRC)/Makefile.msg.targ diff --git a/usr/src/lib/libinstzones/common/instzones_api.h b/usr/src/lib/libinstzones/common/instzones_api.h new file mode 100644 index 0000000000..5fea8c0ce6 --- /dev/null +++ b/usr/src/lib/libinstzones/common/instzones_api.h @@ -0,0 +1,187 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#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 <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <libzonecfg.h> + +/* + * 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_PATCH_ADMIN ((ZLOCKS_T)0x00000004) /* patch 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 char **z_zlist_get_inherited_pkg_dirs(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 boolean_t z_add_inherited_file_system( + char *a_inheritedFileSystem); +extern boolean_t z_path_is_inherited(char *a_path, char a_ftype, + char *a_rootDir); +extern char ** z_get_inherited_file_systems(void); +extern char *z_make_zone_root(char *); +extern void z_path_canonize(char *file); +extern void z_canoninplace(char *file); +extern void z_free_inherited_file_systems(void); + +/* 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 */ diff --git a/usr/src/lib/libinstzones/common/instzones_lib.h b/usr/src/lib/libinstzones/common/instzones_lib.h new file mode 100644 index 0000000000..3dbceabe8f --- /dev/null +++ b/usr/src/lib/libinstzones/common/instzones_lib.h @@ -0,0 +1,387 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +#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 <zone.h> +#include <libzonecfg.h> +#include <libcontract.h> + +/* Local includes */ + +#include "instzones_api.h" + +/* + * 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.*" + +/* this locks all patches, in all zones */ +#define LOBJ_PATCHADMIN "zone.*/patch.*" + +#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 **_zlInheritedDirs; + 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); +char **_z_get_inherited_dirs(char *a_zoneName); +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 */ diff --git a/usr/src/lib/libinstzones/common/llib-linstzones b/usr/src/lib/libinstzones/common/llib-linstzones new file mode 100644 index 0000000000..9d1d8a5f69 --- /dev/null +++ b/usr/src/lib/libinstzones/common/llib-linstzones @@ -0,0 +1,31 @@ +/* + * 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. + */ + + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include "instzones_api.h" diff --git a/usr/src/lib/libinstzones/common/mapfile-vers b/usr/src/lib/libinstzones/common/mapfile-vers new file mode 100644 index 0000000000..4b3e5c32f5 --- /dev/null +++ b/usr/src/lib/libinstzones/common/mapfile-vers @@ -0,0 +1,94 @@ +# +# 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. +# + +# +# 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 +# + +SUNWprivate { + global: + UmountAllZones; + z_add_inherited_file_system; + 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_inherited_file_systems; + z_free_zone_list; + z_get_inherited_file_systems; + 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_path_is_inherited; + 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_inherited_pkg_dirs; + 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; + local: + *; +}; + diff --git a/usr/src/lib/libinstzones/common/zones.c b/usr/src/lib/libinstzones/common/zones.c new file mode 100644 index 0000000000..8b62e15751 --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones.c @@ -0,0 +1,2425 @@ +/* + * 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.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_inherited_pkg_dirs - Determine directories inherited by + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/sysmacros.h> +#include <string.h> +#include <strings.h> +#include <sys/stat.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <signal.h> +#include <stropts.h> +#include <wait.h> +#include <zone.h> +#include <sys/brand.h> +#include <libintl.h> +#include <locale.h> +#include <libzonecfg.h> +#include <libcontract.h> +#include <sys/contract/process.h> +#include <sys/ctfs.h> +#include <assert.h> +#include <dlfcn.h> +#include <link.h> +#include <time.h> + +/* + * 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 list of inherited package directories */ + + if (zelm->_zlInheritedDirs != (char **)NULL) { + int n; + + for (n = 0; + (zelm->_zlInheritedDirs)[n] != (char *)NULL; + n++) { + (void) free((zelm->_zlInheritedDirs)[n]); + } + (void) free(zelm->_zlInheritedDirs); + } + } + + /* 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; + + zlst[numzones]._zlInheritedDirs = + _z_get_inherited_dirs(ze->zone_name); + + 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); + } + } + + /* lock patch administration always */ + + if (a_lflags & ZLOCKS_PATCH_ADMIN) { + b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks, + zoneName, LOBJ_PATCHADMIN, pid, + MSG_ZONES_LCK_THIS_PATCHADM, + ERR_ZONES_LCK_THIS_PATCHADM); + 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. + * (e.g. This happens when we're tyring to patch a zone in an ABE + * that lives on a filesystem that the ABE shares with the currently + * running BE.) + */ + 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); + } + + /* 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 = '\0'; + } + + /* 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 patch administration */ + + if (a_lflags & ZLOCKS_PATCH_ADMIN) { + b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks, + zoneName, LOBJ_PATCHADMIN, ERR_ZONES_ULK_THIS_PATCH); + if (!b) { + errors = B_TRUE; + } + } + + /* 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/patching/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_inherited_pkg_dirs + * Description: Determine directories inherited by 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 + * inherited directories list + * Returns: char ** + * == NULL - zone does not inherit any directories + * - zone index is invalid + * != NULL - array of inherited directories + * NOTE: Any directory list returned is located in static storage that + * must NEVER be free()ed by the caller. + */ + +extern char ** +z_zlist_get_inherited_pkg_dirs(zoneList_t a_zlst, int a_zoneIndex) +{ + int i; + + /* if zones are not implemented, return empty list */ + + if (z_zones_are_implemented() == B_FALSE) { + return (NULL); + } + + /* ignore empty list */ + + if (a_zlst == (zoneList_t)NULL) { + return (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 inherited directories */ + + return (a_zlst[i]._zlInheritedDirs); +} + +/* + * 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); +} diff --git a/usr/src/lib/libinstzones/common/zones_args.c b/usr/src/lib/libinstzones/common/zones_args.c new file mode 100644 index 0000000000..a400cfef9a --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_args.c @@ -0,0 +1,307 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <strings.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <stropts.h> +#include <libintl.h> +#include <locale.h> +#include <assert.h> + +/* + * 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); +} diff --git a/usr/src/lib/libinstzones/common/zones_exec.c b/usr/src/lib/libinstzones/common/zones_exec.c new file mode 100644 index 0000000000..808437285a --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_exec.c @@ -0,0 +1,1120 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <strings.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <signal.h> +#include <wait.h> +#include <stropts.h> +#include <libintl.h> +#include <locale.h> +#include <libcontract.h> +#include <sys/contract/process.h> +#include <sys/ctfs.h> +#include <assert.h> + +/* + * 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)); +} diff --git a/usr/src/lib/libinstzones/common/zones_locks.c b/usr/src/lib/libinstzones/common/zones_locks.c new file mode 100644 index 0000000000..36db482657 --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_locks.c @@ -0,0 +1,995 @@ +/* + * 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_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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <strings.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stropts.h> +#include <libintl.h> +#include <locale.h> +#include <assert.h> + +/* + * 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, "%ld", getpid()); + (void) _z_add_arg(args, "-z"); /* lock valid zone i.d. */ + (void) _z_add_arg(args, "%ld", 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, "<path>", 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, "<path>", 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); + } + } + + /* + * acquire patch lock + */ + + if (a_lflags & ZLOCKS_PATCH_ADMIN) { + + /* + * zone and package administration is locked; lock patch + * administration; if the lock cannot be released, stop, + * release the other locks and return an error + */ + + _z_echoDebug(DBG_ZONES_LCK_ZONE_PATCHADM, a_zlst->_zlName, + LOBJ_PATCHADMIN); + + b = _z_lock_zone_object(&a_zlst->_zlLockObjects, + scratchName, LOBJ_PATCHADMIN, (pid_t)0, + MSG_ZONES_LCK_ZONE_PATCHADM, + ERR_ZONES_LCK_ZONE_PATCHADM); + 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_PATCH_ADMIN) { + /* + * if locked, unlock patch administration lock + * if the lock cannot be released, continue anyway + */ + + _z_echoDebug(DBG_ZONES_ULK_ZONE_PATCHADM, a_zlst->_zlName, + LOBJ_PATCHADMIN); + + b = _z_unlock_zone_object(&a_zlst->_zlLockObjects, + scratchName, LOBJ_PATCHADMIN, + WRN_ZONES_ULK_ZONE_PATCHADM); + if (b == B_FALSE) { + errors = B_TRUE; + } + } + + 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); +} diff --git a/usr/src/lib/libinstzones/common/zones_lofs.c b/usr/src/lib/libinstzones/common/zones_lofs.c new file mode 100644 index 0000000000..2977edc46a --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_lofs.c @@ -0,0 +1,288 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libintl.h> +#include <errno.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/param.h> +#include <libzonecfg.h> +#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'; + } +} diff --git a/usr/src/lib/libinstzones/common/zones_paths.c b/usr/src/lib/libinstzones/common/zones_paths.c new file mode 100644 index 0000000000..ffcae1d40e --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_paths.c @@ -0,0 +1,464 @@ +/* + * 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. + */ + + + +/* + * System includes + */ + +#include <stdio.h> +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <assert.h> +#include <locale.h> +#include <libintl.h> + +/* + * 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] == '/'))) + +/* + * forward declarations + */ + +static char **inheritedFileSystems = (char **)NULL; +static size_t *inheritedFileSystemsLen = (size_t *)NULL; +static int numInheritedFileSystems = 0; + +/* + * ***************************************************************************** + * global external (public) functions + * ***************************************************************************** + */ + +/* + * Name: z_get_inherited_file_systems + * Description: Return list of file systems inherited from the global zone; + * These file systems are entered into the list when the function + * pkgAddInheritedFileSystem() is called. + * Arguments: void + * Returns: char ** + * - pointer to array of character pointers, each pointer + * being a pointer to a string representing a file + * system that is inherited from the global zone + * the last entry will be (char *)NULL + * - (char **)NULL - no file systems inherited + * + */ + +char ** +z_get_inherited_file_systems(void) +{ + return (inheritedFileSystems); +} + +/* + * Name: z_add_inherited_file_system + * Description: Add specified package to internal list of inherited file systems + * Arguments: a_inheritedFileSystem - absolute path to file systen "inherited" + * + * This function is called to register a directory (or + * file system) as being inherited from the global zone + * into the non-global zone being operated on. The + * inherited directory must be specified relative to the + * root file system ("/"). For example, if "/usr" is + * inherited, then the path specified would be "/usr". + * + * Any path subsequently checked for being present in a + * directory inherited read-only from the global zone: + * + * -- will NOT have $PKG_INSTALL_ROOT prepended to it + * -- if $PKG_INSTALL_ROOT is set and $BASEDIR is not set. + * -- WILL have $BASEDIR prepended to it (if set). + * -- $BASEDIR always has $PKG_INSTALL_ROOT included in it. + * -- For example, if $PKG_INSTALL_ROOT is set to /a, and + * -- the base install directory is set to "/opt", then the + * -- $BASEDIR variable will be set to "/a/opt". + * + * Any path that is checked for being present in an inherited + * directory will be specified relative to the root file system + * of the non-global zone in which the path is located. + * + * When a path to update is checked for being present in + * an inherited directory, $PKG_INSTALL_ROOT is stripped + * off the path before it is checked. + * + * If the non-global zone is not running, the scratch zone + * is used to access the non-global zone. In this case, + * $PKG_INSTALL_ROOT will be set to "/a" and both the + * non-global zone's root file system and all inherited + * directories will be mounted on "/a". When a path is checked + * for being inherited, it will have $PKG_INSTALL_ROOT stripped + * from the beginning, so any inherited directories must be + * specified relative to "/" and not $PKG_INSTALL_ROOT. + * + * If the non-global zone is running, the non-global zone + * is used directly. In this case, $PKG_INSTALL_ROOT will + * be set to "/" and both the non-global zone's root file + * system and all inherited directories will be mounted on + * "/". $PKG_INSTALL_ROOT is set to "/" so the path is unchanged + * before being checked against the list of inherited directories. + * + * Returns: boolean_t + * B_TRUE - file system successfully added to list + * B_FALSE - failed to add file system to list + */ + +boolean_t +z_add_inherited_file_system(char *a_inheritedFileSystem) +{ +#define IPSLOP 2 /* for trailing '/' and '\0' */ +#define IPMAX ((sizeof (rp))-IPSLOP) + + char rp[PATH_MAX+1+IPSLOP] = {'\0'}; + int n; + + /* file system cannot be empty */ + + if (a_inheritedFileSystem == NULL || *a_inheritedFileSystem == '\0') { + _z_program_error(ERR_INHERITED_PATH_NULL); + return (B_FALSE); + } + + /* file system must be absolute */ + + if (*a_inheritedFileSystem != '/') { + _z_program_error(ERR_INHERITED_PATH_NOT_ABSOLUTE, + a_inheritedFileSystem); + return (B_FALSE); + } + + /* make a local copy of the path and canonize it */ + + n = strlcpy(rp, a_inheritedFileSystem, IPMAX); + if (n > IPMAX) { + _z_program_error(ERR_INHERITED_PATH_TOO_LONG, + strlen(a_inheritedFileSystem), IPMAX, + a_inheritedFileSystem); + return (B_FALSE); + } + + assert(n > 0); /* path must have at least 1 byte in it */ + + z_path_canonize(rp); /* remove duplicate "/"s, ./, etc */ + + /* add trailing "/" if it's not already there */ + n = strlen(rp); + if (rp[n-1] != '/') { + rp[n++] = '/'; + } + + /* null terminate the string */ + + rp[n] = '\0'; + + /* add file system to internal list */ + + if (inheritedFileSystems == (char **)NULL) { + inheritedFileSystems = (char **)_z_calloc( + 2 * (sizeof (char **))); + inheritedFileSystemsLen = + (size_t *)_z_calloc(2 * (sizeof (size_t *))); + } else { + inheritedFileSystems = (char **)_z_realloc(inheritedFileSystems, + sizeof (char **)*(numInheritedFileSystems+2)); + inheritedFileSystemsLen = (size_t *)_z_realloc( + inheritedFileSystemsLen, + sizeof (size_t *)*(numInheritedFileSystems+2)); + } + + /* add this entry to the end of the list */ + + inheritedFileSystemsLen[numInheritedFileSystems] = strlen(rp); + inheritedFileSystems[numInheritedFileSystems] = _z_strdup(rp); + + numInheritedFileSystems++; + + /* make sure end of the list is properly terminated */ + + inheritedFileSystemsLen[numInheritedFileSystems] = 0; + inheritedFileSystems[numInheritedFileSystems] = (char *)NULL; + + /* exit debugging info */ + + _z_echoDebug(DBG_PATHS_ADD_FS, numInheritedFileSystems, + inheritedFileSystems[numInheritedFileSystems-1]); + + return (B_TRUE); +} + +/* + * Name: z_path_is_inherited + * Description: Determine if the specified path is in a file system that is + * in the internal list of inherited file systems + * Arguments: a_path - pointer to string representing path to verify + * a_ftype - file "type" if known otherwise '\0' + * Type can be "f" (file), or "d" (directory) + * a_rootDir - pointer to string representing root directory where + * a_path is relative to - typically this would either be + * "/" or the path specified as an alternative root to -R + * Returns: boolean_t + * B_TRUE - the path is in inherited file system space + * B_FALSE - the path is NOT in inherited file system space + */ + +boolean_t +z_path_is_inherited(char *a_path, char a_ftype, char *a_rootDir) +{ + int n; + + /* entry assertions */ + + assert(a_path != (char *)NULL); + assert(*a_path != '\0'); + + /* if no inherited file systems, there can be no match */ + + if (numInheritedFileSystems == 0) { + _z_echoDebug(DBG_PATHS_NOT_INHERITED, a_path); + return (B_FALSE); + } + + /* normalize root directory */ + + if ((a_rootDir == (char *)NULL) || (*a_rootDir == '\0')) { + a_rootDir = "/"; + } + + /* + * if a_path resides on an inherited filesystem then + * it must be read-only. + */ + + if (z_isPathWritable(a_path) != 0) { + return (B_FALSE); + } + + /* + * remove the root path from the target path before comparing: + * Example 1: + * -- path is "/export/zone1/root/usr/test" + * -- root path is "/export/zone1/root" + * --- final path should be "/usr/test" + * Example 2: + * -- path is "/usr/test" + * -- root path is "/" + * --- final path should be "/usr/test" + */ + + /* advance past given root directory if path begins with it */ + + n = strlen(a_rootDir); + if (strncmp(a_rootDir, a_path, n) == 0) { + char *p; + + /* advance past the root path */ + + p = a_path + n; + + /* go back to the first occurance of the path separator */ + + while ((*p != '/') && (p > a_path)) { + p--; + } + + /* use this location in the path to compare */ + + a_path = p; + } + + /* + * see if this path is in any inherited file system path + * note that all paths in the inherited list are directories + * so they end in "/" to prevent a partial match, such as + * comparing "/usr/libx" with "/usr/lib" - by making the comparison + * "/usr/libx" with "/usr/lib/" the partial false positive will not + * occur. This complicates matters when the object to compare is a + * directory - in this case, comparing "/usr" with "/usr/" will fail, + * so if the object is a directory, compare one less byte from the + * inherited file system so that the trailing "/" is ignored. + */ + + for (n = 0; n < numInheritedFileSystems; n++) { + int fslen; + + /* get target fs len; adjust -1 if directory */ + + fslen = inheritedFileSystemsLen[n]; + if ((a_ftype == 'd') && (fslen > 1)) { + fslen--; + } + + if (strncmp(a_path, inheritedFileSystems[n], fslen) == 0) { + _z_echoDebug(DBG_PATHS_IS_INHERITED, a_path, + inheritedFileSystems[n]); + return (B_TRUE); + } + } + + /* path is not in inherited file system space */ + + _z_echoDebug(DBG_PATHS_IS_NOT_INHERITED, a_path, a_rootDir); + + return (B_FALSE); +} + +/* + * 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 <zonepath>/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'; +} + +void +z_free_inherited_file_systems(void) +{ + int i; + + for (i = 0; i < numInheritedFileSystems; i++) { + free(inheritedFileSystems[i]); + } + free(inheritedFileSystems); + inheritedFileSystems = NULL; + free(inheritedFileSystemsLen); + inheritedFileSystemsLen = NULL; + numInheritedFileSystems = 0; +} diff --git a/usr/src/lib/libinstzones/common/zones_states.c b/usr/src/lib/libinstzones/common/zones_states.c new file mode 100644 index 0000000000..914af2075c --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_states.c @@ -0,0 +1,585 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <strings.h> +#include <sys/stat.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <stropts.h> +#include <libintl.h> +#include <locale.h> +#include <assert.h> + +/* + * 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); + +} diff --git a/usr/src/lib/libinstzones/common/zones_str.c b/usr/src/lib/libinstzones/common/zones_str.c new file mode 100644 index 0000000000..bc79f0665c --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_str.c @@ -0,0 +1,711 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <strings.h> +#include <stdarg.h> +#include <limits.h> +#include <stropts.h> +#include <libintl.h> +#include <locale.h> +#include <assert.h> + +/* + * 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; +} diff --git a/usr/src/lib/libinstzones/common/zones_strings.h b/usr/src/lib/libinstzones/common/zones_strings.h new file mode 100644 index 0000000000..8c22e1c370 --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_strings.h @@ -0,0 +1,238 @@ +/* + * 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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + + +#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 <libintl.h> + +/* + * 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_PATHS_ADD_FS ILIBSTR("add inherited file system entry <%d> path <%s>") +#define DBG_PATHS_IS_INHERITED ILIBSTR("path <%s> is inherited from <%s>") +#define DBG_PATHS_IS_NOT_INHERITED ILIBSTR("path <%s> in root <%s> not inherited") +#define DBG_PATHS_NOT_INHERITED ILIBSTR("path <%s> not inherited: no inherited file systems") +#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_PATCHADM ILIBSTR("locking patch administration: zone <%s> object <%s>") +#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_PATCHADM ILIBSTR("unlocking patch administration: zone <%s> object <%s>") +#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_INHERITED_PATH_NOT_ABSOLUTE ILIBSTR("inherited file system must be absolute path: <%s>") +#define ERR_INHERITED_PATH_NOT_DIR ILIBSTR("inherited file system <%s> must be absolute path to directory: %s") +#define ERR_INHERITED_PATH_NULL ILIBSTR("empty path specified for inherited file system: must be absolute path") +#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_PKGDIR_GETHANDLE ILIBSTR("unable to get inherited directories: zonecfg_get_handle: %s") +#define ERR_PKGDIR_NOHANDLE ILIBSTR("unable to get inherited directories: zonecfg_init_handle: %s") +#define ERR_PKGDIR_SETIPDENT ILIBSTR("unable to get inherited directories: zonecfg_setipdent: %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_PATCHADM ILIBSTR("Unable to acquire patch administration lock for this system; try again later") +#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_PATCHADM ILIBSTR("Unable to acquire patch administration lock for zone <%s>; please try again later") +#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_PATCH ILIBSTR("Unable to release patch 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_INHERITED_PATH_TOO_LONG ILIBSTR("inherited path too long current length <%d> maximum length <%d> bytes: <%s>") +#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_PATCHADM ILIBSTR("## Waiting for up to <%ld> seconds for patch administration commands to become available (another user is administering patches)") +#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_PATCHADM ILIBSTR("## Waiting for up to <%ld> seconds for patch administration commands to become available (another user is administering patches on zone <%s>)") +#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_PATCHADM ILIBSTR("WARNING: Unable to release patch administration lock for zone <%s>") +#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 */ diff --git a/usr/src/lib/libinstzones/common/zones_utils.c b/usr/src/lib/libinstzones/common/zones_utils.c new file mode 100644 index 0000000000..cd0edec9c4 --- /dev/null +++ b/usr/src/lib/libinstzones/common/zones_utils.c @@ -0,0 +1,689 @@ +/* + * 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.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_get_inherited_dirs - return array of directories inherited by + * specified zone + * _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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <string.h> +#include <strings.h> +#include <sys/stat.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <signal.h> +#include <stropts.h> +#include <libintl.h> +#include <locale.h> +#include <assert.h> +#include <dlfcn.h> + +/* + * 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_get_inherited_dirs + * Description: return array of directories inherited by specified zone + * Arguments: a_zoneName - [RO, *RO] - (char *) + * Pointer to string representing the name of the zone + * to return the list of inherited directories for + * Returns: char ** + * != NULL - list of inherited directories, terminated + * by a NULL pointer + * == NULL - error - unable to retrieve list + */ + +char ** +_z_get_inherited_dirs(char *a_zoneName) +{ + char **dirs = NULL; + int err; + int numIpdents = 0; + struct zone_fstab lookup; + zone_dochandle_t handle = NULL; + + /* entry assertions */ + + assert(a_zoneName != NULL); + assert(*a_zoneName != '\0'); + + /* initialize the zone configuration interface handle */ + + handle = zonecfg_init_handle(); + if (handle == NULL) { + _z_program_error(ERR_PKGDIR_NOHANDLE, + zonecfg_strerror(Z_NOMEM)); + return (NULL); + } + + /* get handle to configuration information for the specified zone */ + + err = zonecfg_get_handle(a_zoneName, handle); + if (err != Z_OK) { + /* If there was no zone before, that's OK */ + if (err != Z_NO_ZONE) { + _z_program_error(ERR_PKGDIR_GETHANDLE, + zonecfg_strerror(err)); + zonecfg_fini_handle(handle); + return (NULL); + } + } + assert(handle != NULL); + + /* get handle to non-global zone ipd enumerator */ + + err = zonecfg_setipdent(handle); + if (err != Z_OK) { + _z_program_error(ERR_PKGDIR_SETIPDENT, zonecfg_strerror(err)); + zonecfg_fini_handle(handle); + return (NULL); + } + + /* enumerate the non-global zone ipd's */ + + while (zonecfg_getipdent(handle, &lookup) == Z_OK) { + dirs = _z_realloc(dirs, sizeof (char **)*(numIpdents+1)); + dirs[numIpdents++] = strdup(lookup.zone_fs_dir); + } + + if (dirs != NULL) { + dirs = _z_realloc(dirs, sizeof (char **)*(numIpdents+1)); + dirs[numIpdents] = NULL; + } + + /* toss non-global zone ipd enumerator handle */ + + (void) zonecfg_endipdent(handle); + + return (dirs); +} + +/* + * 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); +} diff --git a/usr/src/lib/libinstzones/i386/Makefile b/usr/src/lib/libinstzones/i386/Makefile new file mode 100644 index 0000000000..c86be4377c --- /dev/null +++ b/usr/src/lib/libinstzones/i386/Makefile @@ -0,0 +1,28 @@ +# +# 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 ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libinstzones/sparc/Makefile b/usr/src/lib/libinstzones/sparc/Makefile new file mode 100644 index 0000000000..c86be4377c --- /dev/null +++ b/usr/src/lib/libinstzones/sparc/Makefile @@ -0,0 +1,28 @@ +# +# 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 ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) |
