diff options
Diffstat (limited to 'usr/src/cmd')
363 files changed, 28807 insertions, 2003 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index f54e0f9c4b..f2144f91d8 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -21,7 +21,7 @@ # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. -# Copyright (c) 2012 Joyent, Inc. All rights reserved. +# Copyright (c) 2014 Joyent, Inc. All rights reserved. # Copyright (c) 2012 by Delphix. All rights reserved. # Copyright (c) 2013 DEY Storage Systems, Inc. All rights reserved. # Copyright 2014 Garrett D'Amore <garrett@damore.org> @@ -29,8 +29,8 @@ include ../Makefile.master # -# Note that the commands 'lp', and 'perl' are first in -# the list, violating alphabetical order. This is because they are very +# Note that if the 'lp' command were built, it would be first in +# the list, violating alphabetical order. This is because it is very # long-running and should be given the most wall-clock time for a # parallel build. # @@ -51,7 +51,6 @@ FIRST_SUBDIRS= \ COMMON_SUBDIRS= \ allocate \ availdevs \ - lp \ perl \ Adm \ abi \ @@ -94,6 +93,7 @@ COMMON_SUBDIRS= \ cmd-crypto \ cmd-inet \ col \ + column \ compress \ consadm \ coreadm \ @@ -103,6 +103,9 @@ COMMON_SUBDIRS= \ crypt \ csh \ csplit \ + ctfdiff \ + ctfdump \ + ctfmerge \ ctrun \ ctstat \ ctwatch \ @@ -186,7 +189,6 @@ COMMON_SUBDIRS= \ groups \ grpck \ gss \ - hal \ halt \ head \ hostid \ @@ -224,7 +226,6 @@ COMMON_SUBDIRS= \ kvmstat \ last \ lastcomm \ - latencytop \ ldap \ ldapcachemgr \ lgrpinfo \ @@ -245,8 +246,8 @@ COMMON_SUBDIRS= \ look \ ls \ luxadm \ - lvm \ mach \ + machid \ mail \ mailwrapper \ mailx \ @@ -280,6 +281,7 @@ COMMON_SUBDIRS= \ news \ newtask \ nice \ + nicstat \ nl \ nlsadmin \ nohup \ @@ -312,7 +314,6 @@ COMMON_SUBDIRS= \ plockstat \ pr \ prctl \ - print \ printf \ priocntl \ profiles \ @@ -330,7 +331,6 @@ COMMON_SUBDIRS= \ pwck \ pwconv \ pwd \ - pyzfs \ raidctl \ ramdiskadm \ rcap \ @@ -433,8 +433,11 @@ COMMON_SUBDIRS= \ utmp_update \ utmpd \ valtools \ + varpd \ vgrind \ vi \ + vndadm \ + vndstat \ volcheck \ volrmmount \ vrrpadm \ @@ -500,7 +503,6 @@ sparc_SUBDIRS= \ # (see previous comment about 'lp'.) # MSGSUBDIRS= \ - lp \ abi \ acctadm \ allocate \ @@ -612,7 +614,6 @@ MSGSUBDIRS= \ logins \ ls \ luxadm \ - lvm \ mailx \ man \ mesg \ @@ -642,7 +643,6 @@ MSGSUBDIRS= \ power \ pr \ praudit \ - print \ profiles \ projadd \ projects \ @@ -653,7 +653,6 @@ MSGSUBDIRS= \ ptools \ pwconv \ pwd \ - pyzfs \ raidctl \ ramdiskadm \ rcap \ diff --git a/usr/src/cmd/Makefile.check b/usr/src/cmd/Makefile.check index 96fea6b370..f442650fca 100644 --- a/usr/src/cmd/Makefile.check +++ b/usr/src/cmd/Makefile.check @@ -117,7 +117,6 @@ MANIFEST_SUBDIRS= \ krb5/krb5kdc \ krb5/kwarn \ krb5/slave \ - lp/cmd/lpsched \ lvm/rpc.mdcommd \ lvm/rpc.metad \ lvm/rpc.metamedd \ @@ -126,8 +125,6 @@ MANIFEST_SUBDIRS= \ lvm/util \ picl/picld \ pools/poold \ - print/bsd-sysv-commands \ - print/ppdmgr \ rcap/rcapd \ rpcsvc/rpc.bootparamd \ sendmail/lib \ diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services index 37514ac0a7..25ccc7cc43 100644 --- a/usr/src/cmd/cmd-inet/etc/services +++ b/usr/src/cmd/cmd-inet/etc/services @@ -1,6 +1,7 @@ # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2015 Joyent, Inc. # # CDDL HEADER START # @@ -215,6 +216,8 @@ krb5_prop 754/tcp # Kerberos V5 KDC propogation swat 901/tcp # Samba Web Adm.Tool ufsd 1008/tcp ufsd # UFS-aware server ufsd 1008/udp ufsd +portolan 1296/tcp # Portolan +svp-underlay 1339/tcp # SDC VXLAN underlay invalidation cvc 1495/tcp # Network Console ingreslock 1524/tcp www-ldap-gw 1760/tcp # HTTP to LDAP gateway diff --git a/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel b/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel index c62e339953..49151907eb 100644 --- a/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel +++ b/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel @@ -18,6 +18,7 @@ # CDDL HEADER END # # Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, Joyent, Inc. All rights reserved. # # socket configuration information # @@ -52,3 +53,6 @@ 29 4 1 /dev/spdsock 31 1 0 trill + + 33 1 0 lx_netlink + 33 4 0 lx_netlink diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c index 8e9e153b21..9f546bcad9 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014, Joyent, Inc. All rights reserved. */ /* @@ -112,7 +113,9 @@ ipmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, goto fail; } - /* check for solaris.network.interface.config authorization */ + /* + * if not root, check for solaris.network.interface.config authorization + */ if (infop->idi_set) { uid_t uid; struct passwd pwd; @@ -124,24 +127,32 @@ ipmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, goto fail; } uid = ucred_getruid(cred); + ucred_free(cred); if ((int)uid < 0) { err = errno; ipmgmt_log(LOG_ERR, "Could not get user id."); goto fail; } - if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == - NULL) { - err = errno; - ipmgmt_log(LOG_ERR, "Could not get password entry."); - goto fail; - } - if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, - pwd.pw_name) != 1) { - err = EPERM; - ipmgmt_log(LOG_ERR, "Not authorized for operation."); - goto fail; + + /* + * Branded zones may have different auth, but root always + * allowed. + */ + if (uid != 0) { + if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) { + err = errno; + ipmgmt_log(LOG_ERR, + "Could not get password entry."); + goto fail; + } + if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, + pwd.pw_name) != 1) { + err = EPERM; + ipmgmt_log(LOG_ERR, + "Not authorized for operation."); + goto fail; + } } - ucred_free(cred); } /* individual handlers take care of calling door_return */ diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt b/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt index 77b6be9f54..d5812793d4 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt @@ -21,6 +21,7 @@ # # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # # This daemon stores address object to logical interface number mappings # (among other things) and reads/writes from/to ipmgmtd data store. @@ -38,14 +39,16 @@ fi # When the non-global shared-IP stack zone boots, it tries to bring up this # service as well. If we don't start a background process and simply exit the # service, the service will go into maintenance mode and so will all it's -# dependents. +# dependents. Ideally we would simply exit with SMF_EXIT_NODAEMON, but since +# this method is also used in an S10C zone, where support for SMF_EXIT_NODAEMON +# does not exist, we have to stick around. # # In S10C zone (where this script is also used) smf_isnonglobalzone # function is unavailable in smf_include.sh # if [ `/sbin/zonename` != global ]; then if [ `/sbin/zonename -t` = shared ]; then - (while true ; do sleep 3600 ; done) & + (while true ; do sleep 3600 ; done) & exit $SMF_EXIT_OK fi fi diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c index 78da07aebf..9a710f9125 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. * * REQUESTING state of the client state machine. */ @@ -38,6 +39,7 @@ #include <dhcp_hostconf.h> #include <dhcpagent_util.h> #include <dhcpmsg.h> +#include <strings.h> #include "states.h" #include "util.h" @@ -641,8 +643,24 @@ accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp) stop_pkt_retransmission(dsmp); if (*plp->opts[CD_DHCP_TYPE]->value == NAK) { - dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s", - dsmp->dsm_name); + char saddr[18]; + + saddr[0] = '\0'; + if (plp->opts[CD_SERVER_ID] != NULL && + plp->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) { + struct in_addr t_server; + + bcopy(plp->opts[CD_SERVER_ID]->value, &t_server, + plp->opts[CD_SERVER_ID]->len); + (void) strlcpy(saddr, inet_ntoa(t_server), + sizeof (saddr)); + } + + dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s " + "from %s %s", + dsmp->dsm_name, + inet_ntoa(plp->pktfrom.v4.sin_addr), saddr); + dsmp->dsm_bad_offers++; free_pkt_entry(plp); dhcp_restart(dsmp); diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c index ce771e3188..173a8f8325 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c +++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c @@ -1485,7 +1485,7 @@ mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit ptr += sizeof(mDNSs32); nread += sizeof(mDNSs32); } - else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; } + else { return mDNSNULL; } opt++; // increment pointer into rdatabody } diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile index e93209c9b6..fb8c1cdad6 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile @@ -25,7 +25,7 @@ include ../Makefile.com PROG= p12split -LDLIBS += -lwanboot -linetutil -lwanbootutil -lcrypto +LDLIBS += -lwanboot -linetutil -lwanbootutil -lsunw_crypto CPPFLAGS += -I$(CMNCRYPTDIR) all: $(PROG) diff --git a/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile index f4a1f548b7..acad766bd3 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile @@ -25,7 +25,7 @@ include ../Makefile.com PROG = wanboot-cgi -LDLIBS += -lgen -lnsl -lwanbootutil -lnvpair -lwanboot -lcrypto +LDLIBS += -lgen -lnsl -lwanbootutil -lnvpair -lwanboot -lsunw_crypto CPPFLAGS += -I$(CMNCRYPTDIR) all: $(PROG) diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile index 1b82adef80..a743bed065 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile @@ -33,7 +33,7 @@ include ../../../Makefile.cmd ROOTMANIFESTDIR = $(ROOTSVCNETWORK) LDLIBS += -ldladm -ldlpi -all install := LDLIBS += -lcrypto +all install := LDLIBS += -lsunw_crypto LINTFLAGS += -u diff --git a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c index 71a2fc9853..be826baba2 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c @@ -21,10 +21,9 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2012 Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -46,6 +45,7 @@ #include <libscf.h> #include <libscf_priv.h> #include <libuutil.h> +#include <ifaddrs.h> /* * This program moves routing management under SMF. We do this by giving @@ -2335,8 +2335,8 @@ out: /* * - * Return the number of IPv6 addresses configured. This answers the - * generic question, "is IPv6 configured?". We only start in.ndpd if IPv6 + * Return the number of non-loopback IPv6 addresses configured. This answers + * the generic question, "is IPv6 configured?". We only start in.ndpd if IPv6 * is configured, and we also only enable IPv6 routing daemons if IPv6 is * enabled. */ @@ -2344,28 +2344,24 @@ static int ra_numv6intfs(void) { static int num = -1; - int ipsock; - struct lifnum lifn; + int cnt; + struct ifaddrs *ifp_head, *ifp; if (num != -1) return (num); - if ((ipsock = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) { - (void) fprintf(stderr, - gettext("%1$s: unable to open %2$s: %3$s\n"), - myname, IP_DEV_NAME, strerror(errno)); + if (getifaddrs(&ifp_head) < 0) return (0); - } - lifn.lifn_family = AF_INET6; - lifn.lifn_flags = 0; - if (ioctl(ipsock, SIOCGLIFNUM, &lifn) == -1) { - (void) close(ipsock); - return (0); + cnt = 0; + for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) { + if (!(ifp->ifa_flags & IFF_LOOPBACK) && + (ifp->ifa_flags & IFF_IPV6)) + cnt++; } - (void) close(ipsock); - return (num = lifn.lifn_count); + freeifaddrs(ifp_head); + return (num = cnt); } /* diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile index 1d408bccba..5d9ef9e64d 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile @@ -22,6 +22,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2015 Joyent, Inc. # PROG= snoop @@ -39,12 +40,14 @@ OBJS= nfs4_xdr.o snoop.o snoop_aarp.o snoop_adsp.o snoop_aecho.o \ snoop_pppoe.o snoop_rip.o snoop_rip6.o snoop_rpc.o snoop_rpcprint.o \ snoop_rpcsec.o snoop_rport.o snoop_rquota.o snoop_rstat.o snoop_rtmp.o \ snoop_sctp.o snoop_slp.o snoop_smb.o snoop_socks.o snoop_solarnet.o \ - snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o snoop_zip.o + snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o snoop_vxlan.o \ + snoop_zip.o SRCS= $(OBJS:.o=.c) HDRS= snoop.h snoop_mip.h at.h snoop_ospf.h snoop_ospf6.h include ../../../Makefile.cmd +include ../../../Makefile.ctf CPPFLAGS += -I. -I$(SRC)/common/net/dhcp \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c index 097dd6ee90..6d586ab9b5 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c @@ -121,6 +121,7 @@ main(int argc, char **argv) char *output_area; int nbytes; char *datalink = NULL; + char *zonename = NULL; dlpi_handle_t dh; names[0] = '\0'; @@ -227,7 +228,7 @@ main(int argc, char **argv) } (void) setvbuf(stdout, NULL, _IOLBF, BUFSIZ); - while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz")) + while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz:Z")) != EOF) { switch (c) { case 'a': @@ -348,8 +349,11 @@ main(int argc, char **argv) case 'U': Uflg = B_TRUE; break; -#ifdef DEBUG case 'z': + zonename = optarg; + break; +#ifdef DEBUG + case 'Z': zflg = B_TRUE; break; #endif /* DEBUG */ @@ -371,7 +375,7 @@ main(int argc, char **argv) * requested was chosen, but that's too hard. */ if (!icapfile) { - use_kern_pf = open_datalink(&dh, datalink); + use_kern_pf = open_datalink(&dh, datalink, zonename); } else { use_kern_pf = B_FALSE; cap_open_read(icapfile); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h index e4f182572b..42095bcd34 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h @@ -24,6 +24,7 @@ * Use is subject to license terms. * * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Joyent, Inc. */ #ifndef _SNOOP_H @@ -182,7 +183,7 @@ extern void cap_open_read(const char *); extern void cap_open_write(const char *); extern void cap_read(int, int, int, void (*)(), int); extern void cap_close(void); -extern boolean_t open_datalink(dlpi_handle_t *, const char *); +extern boolean_t open_datalink(dlpi_handle_t *, const char *, const char *); extern void init_datalink(dlpi_handle_t, ulong_t, ulong_t, struct timeval *, struct Pf_ext_packetfilt *); extern void net_read(dlpi_handle_t, size_t, int, void (*)(), int); @@ -260,6 +261,7 @@ extern int interpret_socks_reply(int, char *, int); extern int interpret_trill(int, struct ether_header **, char *, int *); extern int interpret_isis(int, char *, int, boolean_t); extern int interpret_bpdu(int, char *, int); +extern int interpret_vxlan(int, char *, int); extern void init_ldap(void); extern boolean_t arp_for_ether(char *, struct ether_addr *); extern char *ether_ouiname(uint32_t); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c index ab6bc292ac..8fbf3fc15f 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c @@ -29,6 +29,7 @@ #include <strings.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <setjmp.h> #include <sys/types.h> #include <sys/signal.h> @@ -114,7 +115,7 @@ select_datalink(const char *linkname, void *arg) * about the datalink useful for building the proper packet filters. */ boolean_t -open_datalink(dlpi_handle_t *dhp, const char *linkname) +open_datalink(dlpi_handle_t *dhp, const char *linkname, const char *zonename) { int retval; int flags = DLPI_PASSIVE | DLPI_RAW; @@ -122,6 +123,9 @@ open_datalink(dlpi_handle_t *dhp, const char *linkname) dlpi_info_t dlinfo; if (linkname == NULL) { + if (zonename != NULL) + pr_err("a datalink must be specified with a zone name"); + /* * Select a datalink to use by default. Prefer datalinks that * are plumbed by IP. @@ -145,7 +149,8 @@ open_datalink(dlpi_handle_t *dhp, const char *linkname) flags |= DLPI_DEVIPNET; if (Iflg || strcmp(linkname, "lo0") == 0) flags |= DLPI_IPNETINFO; - if ((retval = dlpi_open(linkname, dhp, flags)) != DLPI_SUCCESS) { + if ((retval = dlpi_open_zone(linkname, zonename, dhp, + flags)) != DLPI_SUCCESS) { pr_err("cannot open \"%s\": %s", linkname, dlpi_strerror(retval)); } @@ -614,6 +619,10 @@ cap_open_read(const char *name) if (fstat(capfile_in, &st) < 0) pr_err("couldn't stat %s: %m", name); + if (st.st_size > INT_MAX) + pr_err("input file size (%llu bytes) exceeds maximum " + "supported size (%d bytes)", + (unsigned long long)st.st_size, INT_MAX); cap_len = st.st_size; cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c index 5c6bde0cd6..029ed66116 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. */ #include <stdio.h> @@ -54,8 +55,9 @@ static headerlen_fn_t ether_header_len, fddi_header_len, tr_header_len, ib_header_len, ipnet_header_len, ipv4_header_len, ipv6_header_len; -static interpreter_fn_t interpret_ether, interpret_fddi, interpret_tr, +static interpreter_fn_t interpret_fddi, interpret_tr, interpret_ib, interpret_ipnet, interpret_iptun; +interpreter_fn_t interpret_ether; static void addr_copy_swap(struct ether_addr *, struct ether_addr *); static int tr_machdr_len(char *, int *, int *); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c index b84ee3c1b4..c2fcfd7c72 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c @@ -21,6 +21,7 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015, Joyent, Inc. */ #include <stdio.h> @@ -76,6 +77,7 @@ static const struct porttable pt_udp[] = { { 560, "RMONITOR" }, { 561, "MONITOR" }, { IPPORT_SOCKS, "SOCKS" }, + { IPPORT_VXLAN, "VXLAN" }, { 0, NULL } }; @@ -428,6 +430,9 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst, (void) interpret_socks_reply(flags, data, dlen); return (1); + case IPPORT_VXLAN: + (void) interpret_vxlan(flags, data, dlen); + return (1); } } diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c new file mode 100644 index 0000000000..36025ca8f3 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c @@ -0,0 +1,68 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +/* + * Decode VXLAN encapsulated packets. + */ + +#include <sys/vxlan.h> +#include "snoop.h" + +int +interpret_vxlan(int flags, char *data, int fraglen) +{ + vxlan_hdr_t *vxlan = (vxlan_hdr_t *)data; + uint32_t id, vxf; + + if (fraglen < sizeof (vxlan_hdr_t)) { + if (flags & F_SUM) + (void) snprintf(get_sum_line(), MAXLINE, + "VXLAN RUNT"); + if (flags & F_DTAIL) + show_header("VXLAN RUNT: ", "Short packet", fraglen); + + return (fraglen); + } + + id = ntohl(vxlan->vxlan_id) >> VXLAN_ID_SHIFT; + vxf = ntohl(vxlan->vxlan_flags); + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "VXLAN VNI=%d", id); + } + + if (flags & F_DTAIL) { + show_header("VXLAN: ", "VXLAN Header", sizeof (vxlan_hdr_t)); + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Flags = 0x%08x", vxf); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(vxf >> 24, VXLAN_F_VDI >> 24, "vni present", + "vni missing")); + (void) snprintf(get_line(0, 0), get_line_remain(), + "VXLAN network id (VNI) = %d", id); + show_space(); + } + + if (flags & (F_DTAIL | F_ALLSUM)) { + fraglen -= sizeof (vxlan_hdr_t); + data += sizeof (vxlan_hdr_t); + + return (interpret_ether(flags, data, fraglen, fraglen)); + } + + return (0); +} diff --git a/usr/src/cmd/column/Makefile b/usr/src/cmd/column/Makefile new file mode 100644 index 0000000000..ab4cf3390e --- /dev/null +++ b/usr/src/cmd/column/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, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# Use is subject to license terms. +# + +PROG=column + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/column/THIRDPARTYLICENSE b/usr/src/cmd/column/THIRDPARTYLICENSE new file mode 100644 index 0000000000..a80f56cb43 --- /dev/null +++ b/usr/src/cmd/column/THIRDPARTYLICENSE @@ -0,0 +1,26 @@ +Copyright (c) 1989, 1993, 1994 + The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + * +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/usr/src/cmd/column/THIRDPARTYLICENSE.descrip b/usr/src/cmd/column/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..42051a2982 --- /dev/null +++ b/usr/src/cmd/column/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +PORTIONS OF COLUMN COMMAND FUNCTIONALITY diff --git a/usr/src/cmd/column/column.c b/usr/src/cmd/column/column.c new file mode 100644 index 0000000000..4f9a3c81a6 --- /dev/null +++ b/usr/src/cmd/column/column.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Portions Copyright (c) 2013 Joyent, Inc. All rights reserved. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/termios.h> + +#include <err.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#define TAB 8 +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ + +static void c_columnate(void); +static void input(FILE *); +static void maketbl(void); +static void print(void); +static void r_columnate(void); +static void usage(void); +static int width(const wchar_t *); + +static int termwidth = 80; /* default terminal width */ + +static int entries; /* number of records */ +static int eval; /* exit value */ +static int maxlength; /* longest record */ +static wchar_t **list; /* array of pointers to records */ +static const wchar_t *separator = L"\t "; /* field separator for table option */ + +int +main(int argc, char **argv) +{ + struct winsize win; + FILE *fp; + int ch, tflag, xflag; + char *p; + const char *src; + wchar_t *newsep; + size_t seplen; + + (void) setlocale(LC_ALL, ""); + + if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { + if ((p = getenv("COLUMNS"))) + termwidth = atoi(p); + } else + termwidth = win.ws_col; + + tflag = xflag = 0; + while ((ch = getopt(argc, argv, "c:s:tx")) != -1) + switch (ch) { + case 'c': + termwidth = atoi(optarg); + break; + case 's': + src = optarg; + seplen = mbsrtowcs(NULL, &src, 0, NULL); + if (seplen == (size_t)-1) + err(1, "bad separator"); + newsep = malloc((seplen + 1) * sizeof (wchar_t)); + if (newsep == NULL) + err(1, NULL); + (void) mbsrtowcs(newsep, &src, seplen + 1, NULL); + separator = newsep; + break; + case 't': + tflag = 1; + break; + case 'x': + xflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + input(stdin); + else for (; *argv; ++argv) + if ((fp = fopen(*argv, "rF"))) { + input(fp); + (void) fclose(fp); + } else { + warn("%s", *argv); + eval = 1; + } + + if (!entries) + exit(eval); + + maxlength = roundup(maxlength + 1, TAB); + if (tflag) + maketbl(); + else if (maxlength >= termwidth) + print(); + else if (xflag) + c_columnate(); + else + r_columnate(); + exit(eval); + + /*NOTREACHED*/ + return (eval); +} + +static void +c_columnate(void) +{ + int chcnt, col, cnt, endcol, numcols; + wchar_t **lp; + + numcols = termwidth / maxlength; + endcol = maxlength; + for (chcnt = col = 0, lp = list; ; ++lp) { + (void) wprintf(L"%ls", *lp); + chcnt += width(*lp); + if (!--entries) + break; + if (++col == numcols) { + chcnt = col = 0; + endcol = maxlength; + (void) putwchar('\n'); + } else { + while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { + (void) putwchar('\t'); + chcnt = cnt; + } + endcol += maxlength; + } + } + if (chcnt) + (void) putwchar('\n'); +} + +static void +r_columnate(void) +{ + int base, chcnt, cnt, col, endcol, numcols, numrows, row; + + numcols = termwidth / maxlength; + numrows = entries / numcols; + if (entries % numcols) + ++numrows; + + for (row = 0; row < numrows; ++row) { + endcol = maxlength; + for (base = row, chcnt = col = 0; col < numcols; ++col) { + (void) wprintf(L"%ls", list[base]); + chcnt += width(list[base]); + if ((base += numrows) >= entries) + break; + while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { + (void) putwchar('\t'); + chcnt = cnt; + } + endcol += maxlength; + } + (void) putwchar('\n'); + } +} + +static void +print(void) +{ + int cnt; + wchar_t **lp; + + for (cnt = entries, lp = list; cnt--; ++lp) + (void) wprintf(L"%ls\n", *lp); +} + +typedef struct _tbl { + wchar_t **list; + int cols, *len; +} TBL; +#define DEFCOLS 25 + +static void +maketbl(void) +{ + TBL *t; + int coloff, cnt; + wchar_t *p, **lp; + int *lens, maxcols; + TBL *tbl; + wchar_t **cols; + wchar_t *last; + + if ((t = tbl = calloc(entries, sizeof (TBL))) == NULL) + err(1, (char *)NULL); + if ((cols = calloc((maxcols = DEFCOLS), sizeof (*cols))) == NULL) + err(1, (char *)NULL); + if ((lens = calloc(maxcols, sizeof (int))) == NULL) + err(1, (char *)NULL); + for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { + for (coloff = 0, p = *lp; + (cols[coloff] = wcstok(p, separator, &last)); + p = NULL) + if (++coloff == maxcols) { + if (!(cols = realloc(cols, ((uint_t)maxcols + + DEFCOLS) * sizeof (char *))) || + !(lens = realloc(lens, + ((uint_t)maxcols + DEFCOLS) * + sizeof (int)))) + err(1, NULL); + (void) memset((char *)lens + maxcols * + sizeof (int), 0, DEFCOLS * sizeof (int)); + maxcols += DEFCOLS; + } + if ((t->list = calloc(coloff, sizeof (*t->list))) == NULL) + err(1, (char *)NULL); + if ((t->len = calloc(coloff, sizeof (int))) == NULL) + err(1, (char *)NULL); + for (t->cols = coloff; --coloff >= 0; ) { + t->list[coloff] = cols[coloff]; + t->len[coloff] = width(cols[coloff]); + if (t->len[coloff] > lens[coloff]) + lens[coloff] = t->len[coloff]; + } + } + for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { + for (coloff = 0; coloff < t->cols - 1; ++coloff) + (void) wprintf(L"%ls%*ls", t->list[coloff], + lens[coloff] - t->len[coloff] + 2, L" "); + (void) wprintf(L"%ls\n", t->list[coloff]); + } +} + +#define DEFNUM 1000 +#define MAXLINELEN (LINE_MAX + 1) + +static void +input(FILE *fp) +{ + static int maxentry; + int len; + wchar_t *p, buf[MAXLINELEN]; + + if (!list) + if ((list = calloc((maxentry = DEFNUM), sizeof (*list))) == + NULL) + err(1, (char *)NULL); + while (fgetws(buf, MAXLINELEN, fp)) { + for (p = buf; *p && iswspace(*p); ++p) + ; + if (!*p) + continue; + if (!(p = wcschr(p, L'\n'))) { + warnx("line too long"); + eval = 1; + continue; + } + *p = L'\0'; + len = width(buf); + if (maxlength < len) + maxlength = len; + if (entries == maxentry) { + maxentry += DEFNUM; + if (!(list = realloc(list, + (uint_t)maxentry * sizeof (*list)))) + err(1, NULL); + } + list[entries] = malloc((wcslen(buf) + 1) * sizeof (wchar_t)); + if (list[entries] == NULL) + err(1, NULL); + (void) wcscpy(list[entries], buf); + entries++; + } +} + +/* Like wcswidth(), but ignores non-printing characters. */ +static int +width(const wchar_t *wcs) +{ + int w, cw; + + for (w = 0; *wcs != L'\0'; wcs++) + if ((cw = wcwidth(*wcs)) > 0) + w += cw; + return (w); +} + +static void +usage(void) +{ + + (void) fprintf(stderr, + "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); + exit(1); +} diff --git a/usr/src/cmd/coreadm/coreadm.xml b/usr/src/cmd/coreadm/coreadm.xml index 46a4cda17a..28f1e27240 100644 --- a/usr/src/cmd/coreadm/coreadm.xml +++ b/usr/src/cmd/coreadm/coreadm.xml @@ -48,14 +48,6 @@ <service_fmri value='svc:/system/filesystem/minimal' /> </dependency> - <dependency - name='coreadm_manifest-import' - type='service' - grouping='require_all' - restart_on='none'> - <service_fmri value='svc:/system/manifest-import:default' /> - </dependency> - <instance name='default' enabled='false'> <exec_method type='method' diff --git a/usr/src/cmd/cron/Makefile b/usr/src/cmd/cron/Makefile index c9ffeadffe..fb05e292a3 100644 --- a/usr/src/cmd/cron/Makefile +++ b/usr/src/cmd/cron/Makefile @@ -26,6 +26,7 @@ DEFAULTFILES = cron.dfl include ../Makefile.cmd +include ../Makefile.ctf MANIFEST = cron.xml @@ -96,20 +97,20 @@ XPG4ATOBJS= $(ATOBJS:%=objs.xpg4/%) $(XPG4OBJS:%=objs.xpg4/%) XPG6COMMONOBJS= $(COMMONOBJS:%=objs.xpg6/%) XPG6CTOBJS= $(CRONTABOBJS:%=objs.xpg6/%) -cron := POBJS = $(CRONOBJS) $(COMMONOBJ2) -at := POBJS = $(ATOBJS) $(COMMONOBJS) -at.xpg4 := POBJS = $(XPG4ATOBJS) $(XPG4COMMONOBJS) -atrm := POBJS = $(ATRMOBJS) $(COMMONOBJS) -atq := POBJS = $(ATQOBJS) $(COMMONOBJS) -crontab := POBJS = $(CRONTABOBJS) $(COMMONOBJS) -crontab.xpg4 := POBJS = $(XPG4CTOBJS) $(XPG4COMMONOBJS) -crontab.xpg6 := POBJS = $(XPG6CTOBJS) $(XPG6COMMONOBJS) +cron := OBJS = $(CRONOBJS) $(COMMONOBJ2) +at := OBJS = $(ATOBJS) $(COMMONOBJS) +at.xpg4 := OBJS = $(XPG4ATOBJS) $(XPG4COMMONOBJS) +atrm := OBJS = $(ATRMOBJS) $(COMMONOBJS) +atq := OBJS = $(ATQOBJS) $(COMMONOBJS) +crontab := OBJS = $(CRONTABOBJS) $(COMMONOBJS) +crontab.xpg4 := OBJS = $(XPG4CTOBJS) $(XPG4COMMONOBJS) +crontab.xpg6 := OBJS = $(XPG6CTOBJS) $(XPG6COMMONOBJS) CFLAGS += $(CCVERBOSE) NOBJS= $(CRONOBJS) $(ATOBJS) $(ATRMOBJS1) $(ATQOBJS) $(CRONTABOBJS1) \ $(COMMONOBJS) -OBJS = $(NOBJS) $(XPG4COMMONOBJS) $(XPG4ATOBJS) $(XPG4CTOBJS) \ +COBJS = $(NOBJS) $(XPG4COMMONOBJS) $(XPG4ATOBJS) $(XPG4CTOBJS) \ $(XPG6COMMONOBJS) $(XPG6CTOBJS) $(GETRESPOBJ) SRCS = $(NOBJS:%.o=%.c) $(GETRESPSRC) @@ -157,32 +158,35 @@ all : $(PROG) $(XPG4) $(XPG6) $(SCRIPT) $(XPG4SCRIPT) $(FILES) install : all $(ROOTPROG) $(ROOTETCDEFAULTFILES) $(ROOTSYMLINK) \ $(ROOTMANIFEST) $(ROOTMETHOD) -$(PROG) : $$(POBJS) - $(LINK.c) $(POBJS) -o $@ $(LDLIBS) +$(PROG) : $$(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) -$(XPG4) : objs.xpg4 $$(POBJS) - $(LINK.c) $(POBJS) -o $@ $(LDLIBS) +$(XPG4) : objs.xpg4 $$(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) -$(XPG6) : objs.xpg6 $$(POBJS) - $(LINK.c) $(POBJS) -o $@ $(LDLIBS) +$(XPG6) : objs.xpg6 $$(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) objs.xpg6/%.o: %.c $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) objs.xpg6: -@mkdir -p $@ objs.xpg4/%.o: %.c $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) objs.xpg4: -@mkdir -p $@ objs.xpg4/values-xpg4.o: ../../lib/common/common/values-xpg4.c $(COMPILE.c) -o $@ ../../lib/common/common/values-xpg4.c + $(POST_PROCESS_O) %.o: $(SRC)/common/util/%.c $(COMPILE.c) $(OUTPUT_OPTION) $< @@ -219,7 +223,7 @@ $(POFILE): $(POFILES) $(RM) $@; cat $(POFILES) > $@ clean : - $(RM) $(OBJS) att1.h att1.c att2.c + $(RM) $(COBJS) att1.h att1.c att2.c lint : lint_SRCS diff --git a/usr/src/cmd/cron/cron.c b/usr/src/cmd/cron/cron.c index ab36d09037..33a7373f1e 100644 --- a/usr/src/cmd/cron/cron.c +++ b/usr/src/cmd/cron/cron.c @@ -23,7 +23,7 @@ * Use is subject to license terms. * * Copyright 2013 Joshua M. Clulow <josh@sysmgr.org> - * + * Copyright 2013 Joyent, Inc. All rights reserved. * Copyright (c) 2014 Gary Mills */ @@ -315,7 +315,8 @@ static int ex(struct event *e); static void read_dirs(int); static void mail(char *, char *, int); static char *next_field(int, int); -static void readcron(struct usr *, time_t); +static void readcron(char *, struct usr *, time_t); +static void readcronfile(FILE *, struct usr *, time_t); static int next_ge(int, char *); static void free_if_unused(struct usr *); static void del_atjob(char *, char *); @@ -420,7 +421,7 @@ extern void el_delete(void); static int valid_entry(char *, int); static struct usr *create_ulist(char *, int); -static void init_cronevent(char *, int); +static void init_cronevent(char *, char *); static void init_atevent(char *, time_t, int, int); static void update_atevent(struct usr *, char *, time_t, int); @@ -759,6 +760,18 @@ read_dirs(int first) time_t tim; + if (chdir(SYSCRONDIR) != -1) { + cwd = CRON; + if ((dir = opendir(".")) != NULL) { + while ((dp = readdir(dir)) != NULL) { + if (!valid_entry(dp->d_name, CRONEVENT)) + continue; + init_cronevent(SYSCRONDIR, dp->d_name); + } + (void) closedir(dir); + } + } + if (chdir(CRONDIR) == -1) crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG); cwd = CRON; @@ -767,7 +780,7 @@ read_dirs(int first) while ((dp = readdir(dir)) != NULL) { if (!valid_entry(dp->d_name, CRONEVENT)) continue; - init_cronevent(dp->d_name, first); + init_cronevent(CRONDIR, dp->d_name); } (void) closedir(dir); @@ -853,23 +866,18 @@ create_ulist(char *name, int type) } void -init_cronevent(char *name, int first) +init_cronevent(char *basedir, char *name) { struct usr *u; - if (first) { + if ((u = find_usr(name)) == NULL) { u = create_ulist(name, CRONEVENT); - readcron(u, 0); + readcron(basedir, u, 0); } else { - if ((u = find_usr(name)) == NULL) { - u = create_ulist(name, CRONEVENT); - readcron(u, 0); - } else { - u->ctexists = TRUE; - rm_ctevents(u); - el_remove(u->ctid, 0); - readcron(u, 0); - } + u->ctexists = TRUE; + rm_ctevents(u); + el_remove(u->ctid, 0); + readcron(basedir, u, 0); } } @@ -950,7 +958,7 @@ mod_ctab(char *name, time_t reftime) (void) strcpy(u->home, pw->pw_dir); u->uid = pw->pw_uid; u->gid = pw->pw_gid; - readcron(u, reftime); + readcron(CRONDIR, u, reftime); } else { u->uid = pw->pw_uid; u->gid = pw->pw_gid; @@ -973,7 +981,7 @@ mod_ctab(char *name, time_t reftime) /* user didnt have a crontab last time */ u->ctid = ecid++; u->ctevents = NULL; - readcron(u, reftime); + readcron(CRONDIR, u, reftime); return; } #ifdef DEBUG @@ -981,7 +989,7 @@ mod_ctab(char *name, time_t reftime) #endif rm_ctevents(u); el_remove(u->ctid, 0); - readcron(u, reftime); + readcron(CRONDIR, u, reftime); } } @@ -1116,8 +1124,94 @@ update_atevent(struct usr *u, char *name, time_t tim, int jobtype) static char line[CTLINESIZE]; /* holds a line from a crontab file */ static int cursor; /* cursor for the above line */ +static int +copyfile(char *name, FILE *dp) +{ + FILE *tf; + + if ((tf = fopen(name, "r")) == NULL) { + (void) fclose(dp); + return (1); + } + + while (fgets(line, CTLINESIZE, tf) != NULL) { + if (fputs(line, dp) == EOF) { + (void) fclose(tf); + (void) fclose(dp); + return (1); + } + } + (void) fclose(tf); + + return (0); +} + +static void +readcron(char *basedir, struct usr *u, time_t reftime) +{ + char *altpath; + struct stat sb; + FILE *cf; /* cf will be a user's crontab file */ + char altnamebuf[PATH_MAX]; + char namebuf[PATH_MAX]; + + if (strcmp(basedir, SYSCRONDIR) == 0) + altpath = CRONDIR; + else + altpath = SYSCRONDIR; + + if (snprintf(altnamebuf, sizeof (altnamebuf), "%s/%s", altpath, + u->name) >= sizeof (altnamebuf)) + return; + + if (snprintf(namebuf, sizeof (namebuf), "%s/%s", basedir, u->name) >= + sizeof (namebuf)) + return; + + if (stat(altnamebuf, &sb) != -1) { + /* + * There is a secondary crontab for this user. We need to + * merge the two crontabs into a temporary file for loading. + */ + int fd; + char tmpfile[PATH_MAX]; + + (void) strlcpy(tmpfile, "/tmp/cronXXXXXX", sizeof (tmpfile)); + if ((fd = mkstemp(tmpfile)) == -1) + return; + + unlink(tmpfile); + if ((cf = fdopen(fd, "w+")) == NULL) { + close(fd); + return; + } + + if (copyfile(namebuf, cf) != 0) + return; + + if (copyfile(altnamebuf, cf) != 0) + return; + + (void) fflush(cf); + rewind(cf); + + } else { + /* + * Only one crontab, open it directly. + */ + if ((cf = fopen(namebuf, "r")) == NULL) { + mail(u->name, NOREAD, ERR_UNIXERR); + return; + } + } + + readcronfile(cf, u, reftime); + + (void) fclose(cf); +} + static void -readcron(struct usr *u, time_t reftime) +readcronfile(FILE *cf, struct usr *u, time_t reftime) { /* * readcron reads in a crontab file for a user (u). The list of @@ -1125,12 +1219,9 @@ readcron(struct usr *u, time_t reftime) * this list. Each event is also entered into the main event * list. */ - FILE *cf; /* cf will be a user's crontab file */ struct event *e; int start; unsigned int i; - char namebuf[PATH_MAX]; - char *pname; struct shared *tz = NULL; struct shared *home = NULL; struct shared *shell = NULL; @@ -1138,19 +1229,6 @@ readcron(struct usr *u, time_t reftime) /* read the crontab file */ cte_init(); /* Init error handling */ - if (cwd != CRON) { - if (snprintf(namebuf, sizeof (namebuf), "%s/%s", - CRONDIR, u->name) >= sizeof (namebuf)) { - return; - } - pname = namebuf; - } else { - pname = u->name; - } - if ((cf = fopen(pname, "r")) == NULL) { - mail(u->name, NOREAD, ERR_UNIXERR); - return; - } while (fgets(line, CTLINESIZE, cf) != NULL) { char *tmp; /* process a line of a crontab file */ @@ -1279,7 +1357,6 @@ again: #endif } cte_sendmail(u->name); /* mail errors if any to user */ - (void) fclose(cf); rel_shared(tz); rel_shared(shell); rel_shared(home); @@ -2442,6 +2519,9 @@ ex(struct event *e) } else { r = audit_cron_session(e->u->name, CRONDIR, e->u->uid, e->u->gid, NULL); + if (r != 0) + r = audit_cron_session(e->u->name, SYSCRONDIR, + e->u->uid, e->u->gid, NULL); } if (r != 0) { msg("cron audit problem. job failed (%s) for user %s", diff --git a/usr/src/cmd/cron/cron.h b/usr/src/cmd/cron/cron.h index a76016299c..93e21e7b41 100644 --- a/usr/src/cmd/cron/cron.h +++ b/usr/src/cmd/cron/cron.h @@ -71,6 +71,9 @@ struct message { char logname[LLEN]; }; +/* anything below here can be changed */ + +#define SYSCRONDIR "/etc/cron.d/crontabs" #define CRONDIR "/var/spool/cron/crontabs" #define ATDIR "/var/spool/cron/atjobs" #define ACCTFILE "/var/cron/log" diff --git a/usr/src/cmd/cron/crontab.c b/usr/src/cmd/cron/crontab.c index 327a71388b..cdb4e1e394 100644 --- a/usr/src/cmd/cron/crontab.c +++ b/usr/src/cmd/cron/crontab.c @@ -71,7 +71,7 @@ "usage:\n" \ "\tcrontab [file]\n" \ "\tcrontab -e [username]\n" \ - "\tcrontab -l [username]\n" \ + "\tcrontab -l [-g] [username]\n" \ "\tcrontab -r [username]" #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)." #define NOTALLOWED "you are not authorized to use cron. Sorry." @@ -120,6 +120,7 @@ main(int argc, char **argv) int c, r; int rflag = 0; int lflag = 0; + int gflag = 0; int eflag = 0; int errflg = 0; char *pp; @@ -151,11 +152,14 @@ main(int argc, char **argv) exit(1); } - while ((c = getopt(argc, argv, "elr")) != EOF) + while ((c = getopt(argc, argv, "eglr")) != EOF) switch (c) { case 'e': eflag++; break; + case 'g': + gflag++; + break; case 'l': lflag++; break; @@ -170,6 +174,9 @@ main(int argc, char **argv) if (eflag + lflag + rflag > 1) errflg++; + if (gflag && !lflag) + errflg++; + argc -= optind; argv += optind; if (errflg || argc > 1) @@ -236,12 +243,27 @@ main(int argc, char **argv) exit(0); } if (lflag) { - if ((fp = fopen(cf, "r")) == NULL) - crabort(BADOPEN); - while (fgets(line, CTLINESIZE, fp) != NULL) - fputs(line, stdout); - fclose(fp); - exit(0); + char sysconf[PATH_MAX]; + + if (gflag) { + if (snprintf(sysconf, sizeof (sysconf), "%s/%s", + SYSCRONDIR, login) < sizeof (sysconf) && + (fp = fopen(sysconf, "r")) != NULL) { + while (fgets(line, CTLINESIZE, fp) != NULL) + fputs(line, stdout); + fclose(fp); + exit(0); + } else { + crabort(BADOPEN); + } + } else { + if ((fp = fopen(cf, "r")) == NULL) + crabort(BADOPEN); + while (fgets(line, CTLINESIZE, fp) != NULL) + fputs(line, stdout); + fclose(fp); + exit(0); + } } if (eflag) { if ((fp = fopen(cf, "r")) == NULL) { diff --git a/usr/src/cmd/ctfdiff/Makefile b/usr/src/cmd/ctfdiff/Makefile new file mode 100644 index 0000000000..268bf9f3ed --- /dev/null +++ b/usr/src/cmd/ctfdiff/Makefile @@ -0,0 +1,33 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015, Joyent, Inc. +# + +PROG= ctfdiff + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) +LDLIBS += -lctf + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/ctfdiff/ctfdiff.c b/usr/src/cmd/ctfdiff/ctfdiff.c new file mode 100644 index 0000000000..2537c15bcb --- /dev/null +++ b/usr/src/cmd/ctfdiff/ctfdiff.c @@ -0,0 +1,508 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015, Joyent, Inc. + */ + +/* + * diff two CTF containers + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <strings.h> +#include <libctf.h> +#include <libgen.h> +#include <stdarg.h> + +#define CTFDIFF_NAMELEN 256 + +#define CTFDIFF_EXIT_SIMILAR 0 +#define CTFDIFF_EXIT_DIFFERENT 1 +#define CTFDIFF_EXIT_USAGE 2 +#define CTFDIFF_EXIT_ERROR 3 + +typedef enum ctf_diff_cmd { + CTF_DIFF_TYPES = 0x01, + CTF_DIFF_FUNCS = 0x02, + CTF_DIFF_OBJS = 0x04, + CTF_DIFF_DEFAULT = 0x07, + CTF_DIFF_LABEL = 0x08, + CTF_DIFF_ALL = 0x0f +} ctf_diff_cmd_t; + +typedef struct { + int dil_next; + const char **dil_labels; +} diff_label_t; + +static char *g_progname; +static const char *g_iname; +static ctf_file_t *g_ifp; +static const char *g_oname; +static ctf_file_t *g_ofp; +static char **g_typelist = NULL; +static int g_nexttype = 0; +static int g_ntypes = 0; +static char **g_objlist = NULL; +static int g_nextfunc = 0; +static int g_nfuncs = 0; +static char **g_funclist = NULL; +static int g_nextobj = 0; +static int g_nobjs = 0; +static boolean_t g_onlydiff = B_FALSE; +static boolean_t g_different = B_FALSE; +static ctf_diff_cmd_t g_flag = 0; + +static void +ctfdiff_fatal(const char *fmt, ...) +{ + va_list ap; + + (void) fprintf(stderr, "%s: ", g_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(CTFDIFF_EXIT_ERROR); +} + +static const char * +fp_to_name(ctf_file_t *fp) +{ + if (fp == g_ifp) + return (g_iname); + if (fp == g_ofp) + return (g_oname); + return (NULL); +} + +/* ARGSUSED */ +static void +diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp, + ulong_t oidx, void *arg) +{ + char namebuf[CTFDIFF_NAMELEN]; + + if (similar == B_TRUE) + return; + + if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) { + if (g_nextfunc != 0) + return; + (void) printf("ctf container %s function %ld is different\n", + fp_to_name(ifp), iidx); + } else { + if (g_nextfunc != 0) { + int i; + for (i = 0; i < g_nextfunc; i++) { + if (strcmp(g_funclist[i], namebuf) == 0) + break; + } + if (i == g_nextfunc) + return; + } + (void) printf("ctf container %s function %s (%ld) is " + "different\n", fp_to_name(ifp), namebuf, iidx); + } + + g_different = B_TRUE; +} + +/* ARGSUSED */ +static void +diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar, + ctf_file_t *ofp, ulong_t oidx, ctf_id_t oid, void *arg) +{ + char namebuf[CTFDIFF_NAMELEN]; + + if (similar == B_TRUE) + return; + + if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) { + if (g_nextobj != 0) + return; + (void) printf("ctf container %s object %ld is different\n", + fp_to_name(ifp), iidx); + } else { + if (g_nextobj != 0) { + int i; + for (i = 0; i < g_nextobj; i++) { + if (strcmp(g_objlist[i], namebuf) == 0) + break; + } + if (i == g_nextobj) + return; + } + (void) printf("ctf container %s object %s (%ld) is different\n", + fp_to_name(ifp), namebuf, iidx); + } + + g_different = B_TRUE; +} + +/* ARGSUSED */ +static void +diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp, + ctf_id_t oid, void *arg) +{ + if (similar == B_TRUE) + return; + + if (ctf_type_kind(ifp, iid) == CTF_K_UNKNOWN) + return; + + /* + * Check if it's the type the user cares about. + */ + if (g_nexttype != 0) { + int i; + char namebuf[CTFDIFF_NAMELEN]; + + if (ctf_type_name(ifp, iid, namebuf, sizeof (namebuf)) == + NULL) { + ctfdiff_fatal("failed to obtain the name " + "of type %ld from %s: %s\n", + iid, fp_to_name(ifp), + ctf_errmsg(ctf_errno(ifp))); + } + + for (i = 0; i < g_nexttype; i++) { + if (strcmp(g_typelist[i], namebuf) == 0) + break; + } + + if (i == g_nexttype) + return; + } + + g_different = B_TRUE; + + if (g_onlydiff == B_TRUE) + return; + + (void) printf("ctf container %s type %ld is different\n", + fp_to_name(ifp), iid); +} + +/* ARGSUSED */ +static int +diff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg) +{ + uint32_t *count = arg; + *count = *count + 1; + + return (0); +} + +/* ARGSUSED */ +static int +diff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg) +{ + diff_label_t *dil = arg; + + dil->dil_labels[dil->dil_next] = name; + dil->dil_next++; + + return (0); +} + +static int +diff_labels(ctf_file_t *ifp, ctf_file_t *ofp) +{ + int ret; + uint32_t nilabel, nolabel, i, j; + diff_label_t idl, odl; + const char **ilptr, **olptr; + + nilabel = nolabel = 0; + ret = ctf_label_iter(ifp, diff_labels_count, &nilabel); + if (ret == CTF_ERR) + return (ret); + ret = ctf_label_iter(ofp, diff_labels_count, &nolabel); + if (ret == CTF_ERR) + return (ret); + + if (nilabel != nolabel) { + (void) printf("ctf container %s labels differ from ctf " + "container %s\n", fp_to_name(ifp), fp_to_name(ofp)); + g_different = B_TRUE; + return (0); + } + + if (nilabel == 0) + return (0); + + ilptr = malloc(sizeof (char *) * nilabel); + olptr = malloc(sizeof (char *) * nolabel); + if (ilptr == NULL || olptr == NULL) { + ctfdiff_fatal("failed to allocate memory for label " + "comparison\n"); + } + + idl.dil_next = 0; + idl.dil_labels = ilptr; + odl.dil_next = 0; + odl.dil_labels = olptr; + + if ((ret = ctf_label_iter(ifp, diff_labels_fill, &idl)) != 0) + goto out; + if ((ret = ctf_label_iter(ofp, diff_labels_fill, &odl)) != 0) + goto out; + + for (i = 0; i < nilabel; i++) { + for (j = 0; j < nolabel; j++) { + if (strcmp(ilptr[i], olptr[j]) == 0) + break; + } + + if (j == nolabel) { + (void) printf("ctf container %s labels differ from ctf " + "container %s\n", fp_to_name(ifp), fp_to_name(ofp)); + g_different = B_TRUE; + break; + } + } + + ret = 0; +out: + free(ilptr); + free(olptr); + return (ret); +} + +static void +diff_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", g_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-afIloqt] [-F function] [-O object]" + "[-p parent] [-P parent]\n" + "\t[-T type] file1 file2\n" + "\n" + "\t-a diff label, types, objects, and functions\n" + "\t-f diff function type information\n" + "\t-F when diffing functions, only consider those named\n" + "\t-I ignore the names of integral types\n" + "\t-l diff CTF labels\n" + "\t-o diff global object type information\n" + "\t-O when diffing objects, only consider those named\n" + "\t-p set the CTF parent for file1\n" + "\t-P set the CTF parent for file2\n" + "\t-q set quiet mode (no diff information sent to stdout)\n" + "\t-t diff CTF type information\n" + "\t-T when diffing types, only consider those named\n", + g_progname); +} + +int +main(int argc, char *argv[]) +{ + ctf_diff_flag_t flags = 0; + int err, c; + ctf_file_t *ifp, *ofp; + ctf_diff_t *cdp; + ctf_file_t *pifp = NULL; + ctf_file_t *pofp = NULL; + + g_progname = basename(argv[0]); + + while ((c = getopt(argc, argv, "aqtfolIp:F:O:P:T:")) != -1) { + switch (c) { + case 'a': + g_flag |= CTF_DIFF_ALL; + break; + case 't': + g_flag |= CTF_DIFF_TYPES; + break; + case 'f': + g_flag |= CTF_DIFF_FUNCS; + break; + case 'o': + g_flag |= CTF_DIFF_OBJS; + break; + case 'l': + g_flag |= CTF_DIFF_LABEL; + break; + case 'q': + g_onlydiff = B_TRUE; + break; + case 'p': + pifp = ctf_open(optarg, &err); + if (pifp == NULL) { + ctfdiff_fatal("failed to open parent input " + "container %s: %s\n", optarg, + ctf_errmsg(err)); + } + break; + case 'F': + if (g_nextfunc == g_nfuncs) { + if (g_nfuncs == 0) + g_nfuncs = 16; + else + g_nfuncs *= 2; + g_funclist = realloc(g_funclist, + sizeof (char *) * g_nfuncs); + if (g_funclist == NULL) { + ctfdiff_fatal("failed to allocate " + "memory for the %dth -F option: " + "%s\n", g_nexttype + 1, + strerror(errno)); + } + } + g_funclist[g_nextfunc] = optarg; + g_nextfunc++; + break; + case 'O': + if (g_nextobj == g_nobjs) { + if (g_nobjs == 0) + g_nobjs = 16; + else + g_nobjs *= 2; + g_objlist = realloc(g_objlist, + sizeof (char *) * g_nobjs); + if (g_objlist == NULL) { + ctfdiff_fatal("failed to allocate " + "memory for the %dth -F option: " + "%s\n", g_nexttype + 1, + strerror(errno)); + return (CTFDIFF_EXIT_ERROR); + } + } + g_objlist[g_nextobj] = optarg; + g_nextobj++; + break; + case 'I': + flags |= CTF_DIFF_F_IGNORE_INTNAMES; + break; + case 'P': + pofp = ctf_open(optarg, &err); + if (pofp == NULL) { + ctfdiff_fatal("failed to open parent output " + "container %s: %s\n", optarg, + ctf_errmsg(err)); + } + break; + case 'T': + if (g_nexttype == g_ntypes) { + if (g_ntypes == 0) + g_ntypes = 16; + else + g_ntypes *= 2; + g_typelist = realloc(g_typelist, + sizeof (char *) * g_ntypes); + if (g_typelist == NULL) { + ctfdiff_fatal("failed to allocate " + "memory for the %dth -T option: " + "%s\n", g_nexttype + 1, + strerror(errno)); + } + } + g_typelist[g_nexttype] = optarg; + g_nexttype++; + } + } + + argc -= optind - 1; + argv += optind - 1; + + if (g_flag == 0) + g_flag = CTF_DIFF_DEFAULT; + + if (argc != 3) { + diff_usage(NULL); + return (CTFDIFF_EXIT_USAGE); + } + + if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) { + diff_usage("-T cannot be used if not diffing types\n"); + return (CTFDIFF_EXIT_USAGE); + } + + if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) { + diff_usage("-F cannot be used if not diffing functions\n"); + return (CTFDIFF_EXIT_USAGE); + } + + if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) { + diff_usage("-O cannot be used if not diffing objects\n"); + return (CTFDIFF_EXIT_USAGE); + } + + ifp = ctf_open(argv[1], &err); + if (ifp == NULL) { + ctfdiff_fatal("failed to open %s: %s\n", argv[1], + ctf_errmsg(err)); + } + if (pifp != NULL) { + err = ctf_import(ifp, pifp); + if (err != 0) { + ctfdiff_fatal("failed to set parent container: %s\n", + ctf_errmsg(ctf_errno(pifp))); + } + } + g_iname = argv[1]; + g_ifp = ifp; + + ofp = ctf_open(argv[2], &err); + if (ofp == NULL) { + ctfdiff_fatal("failed to open %s: %s\n", argv[2], + ctf_errmsg(err)); + } + + if (pofp != NULL) { + err = ctf_import(ofp, pofp); + if (err != 0) { + ctfdiff_fatal("failed to set parent container: %s\n", + ctf_errmsg(ctf_errno(pofp))); + } + } + g_oname = argv[2]; + g_ofp = ofp; + + if (ctf_diff_init(ifp, ofp, &cdp) != 0) { + ctfdiff_fatal("failed to initialize libctf diff engine: %s\n", + ctf_errmsg(ctf_errno(ifp))); + } + + if (ctf_diff_setflags(cdp, flags) != 0) { + ctfdiff_fatal("failed to set ctfdiff flags: %s\n", + ctf_errmsg(ctf_errno(ifp))); + } + + err = 0; + if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR) + err = ctf_diff_types(cdp, diff_cb, NULL); + if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR) + err = ctf_diff_functions(cdp, diff_func_cb, NULL); + if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR) + err = ctf_diff_objects(cdp, diff_obj_cb, NULL); + if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR) + err = diff_labels(ifp, ofp); + + ctf_diff_fini(cdp); + if (err == CTF_ERR) { + ctfdiff_fatal("encountered a libctf error: %s!\n", + ctf_errmsg(ctf_errno(ifp))); + } + + return (g_different == B_TRUE ? CTFDIFF_EXIT_DIFFERENT : + CTFDIFF_EXIT_SIMILAR); +} diff --git a/usr/src/cmd/ctfdump/Makefile b/usr/src/cmd/ctfdump/Makefile new file mode 100644 index 0000000000..962ca43a8f --- /dev/null +++ b/usr/src/cmd/ctfdump/Makefile @@ -0,0 +1,33 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2015, Joyent, Inc. +# + +PROG= ctfdump + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) +LDLIBS += -lctf + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c new file mode 100644 index 0000000000..327da82d63 --- /dev/null +++ b/usr/src/cmd/ctfdump/ctfdump.c @@ -0,0 +1,803 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015, Joyent, Inc. + */ + +/* + * Dump information about CTF containers. This was inspired by the original + * ctfdump written in tools/ctf, but this has been reimplemented in terms of + * libctf. + */ + +#include <stdio.h> +#include <unistd.h> +#include <libctf.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <sys/sysmacros.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +typedef enum ctfdump_arg { + CTFDUMP_OBJECTS = 0x01, + CTFDUMP_FUNCTIONS = 0x02, + CTFDUMP_HEADER = 0x04, + CTFDUMP_LABELS = 0x08, + CTFDUMP_STRINGS = 0x10, + CTFDUMP_STATS = 0x20, + CTFDUMP_TYPES = 0x40, + CTFDUMP_DEFAULT = 0x7f, + CTFDUMP_OUTPUT = 0x80, + CTFDUMP_ALL = 0xff +} ctfdump_arg_t; + +typedef struct ctfdump_stat { + ulong_t cs_ndata; /* number of data objects */ + ulong_t cs_nfuncs; /* number of functions */ + ulong_t cs_nfuncargs; /* number of function args */ + ulong_t cs_nfuncmax; /* largest number of args */ + ulong_t cs_ntypes[CTF_K_MAX]; /* number of types */ + ulong_t cs_nsmembs; /* number of struct members */ + ulong_t cs_nsmax; /* largest number of members */ + ulong_t cs_structsz; /* sum of structures sizes */ + ulong_t cs_sszmax; /* largest structure */ + ulong_t cs_numembs; /* number of union members */ + ulong_t cs_numax; /* largest number of members */ + ulong_t cs_unionsz; /* sum of unions sizes */ + ulong_t cs_uszmax; /* largest union */ + ulong_t cs_nemembs; /* number of enum members */ + ulong_t cs_nemax; /* largest number of members */ + ulong_t cs_nstrings; /* number of strings */ + ulong_t cs_strsz; /* string size */ + ulong_t cs_strmax; /* longest string */ +} ctfdump_stat_t; + +static const char *g_progname; +static ctfdump_arg_t g_dump; +static ctf_file_t *g_fp; +static ctfdump_stat_t g_stats; +static ctf_id_t *g_fargc; +static int g_nfargc; + +static const char *ctfdump_fpenc[] = { + NULL, + "SINGLE", + "DOUBLE", + "COMPLEX", + "DCOMPLEX", + "LDCOMPLEX", + "LDOUBLE", + "INTERVAL", + "DINTERVAL", + "LDINTERVAL", + "IMAGINARY", + "DIMAGINARY", + "LDIMAGINARY" +}; + +static const char *ctfdump_knames[CTF_K_MAX+1] = { + "unknown", "integer", "float", "pointer", + "array", "function", "struct", "union", + "enum", "forward", "typedef", "volatile", + "const", "restrict", "CTF_K_14", "CTF_K_15", + "CTF_K_16", "CTF_K_17", "CTF_K_18", "CTF_K_19", + "CTF_K_20", "CTF_K_21", "CTF_K_22", "CTF_K_23", + "CTF_K_24", "CTF_K_25", "CTF_K_26", "CTF_K_27", + "CTF_K_28", "CTF_K_29", "CTF_K_30", "CTF_K_MAX" +}; + +/* + * When stats are requested, we have to go through everything. To make our lives + * easier, we'll just always allow the code to print everything out, but only + * output it if we have actually enabled that section. + */ +static void +ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...) +{ + va_list ap; + + if ((arg & g_dump) == 0) + return; + + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); +} + +static void +ctfdump_fatal(const char *fmt, ...) +{ + va_list ap; + + (void) fprintf(stderr, "%s: ", g_progname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(1); +} + +static void +ctfdump_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + (void) fprintf(stderr, "%s: ", g_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-dfhlsSt] [-p parent] [-u outfile] " + "file\n" + "\n" + "\t-d dump object data\n" + "\t-f dump function data\n" + "\t-h dump the CTF header\n" + "\t-l dump the label table\n" + "\t-p use parent to supply additional information\n" + "\t-s dump the string table\n" + "\t-S dump statistics about the CTF container\n" + "\t-t dump type information\n" + "\t-u dump uncompressed CTF data to outfile\n", + g_progname); +} + +static void +ctfdump_title(ctfdump_arg_t arg, const char *header) +{ + static const char line[] = "----------------------------------------" + "----------------------------------------"; + ctfdump_printf(arg, "\n- %s %.*s\n\n", header, (int)78 - strlen(header), + line); +} + +static int +ctfdump_objects_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg) +{ + int len; + + len = snprintf(NULL, 0, " [%u] %u", g_stats.cs_ndata, id); + ctfdump_printf(CTFDUMP_OBJECTS, " [%u] %u %*s%s (%u)\n", + g_stats.cs_ndata, id, MAX(15 - len, 0), "", name, symidx); + g_stats.cs_ndata++; + return (0); +} + +static void +ctfdump_objects(void) +{ + ctfdump_title(CTFDUMP_OBJECTS, "Data Objects"); + if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR) + ctfdump_fatal("failed to dump objects: %s\n", + ctf_errmsg(ctf_errno(g_fp))); +} + +static void +ctfdump_fargs_grow(int nargs) +{ + if (g_nfargc < nargs) { + g_fargc = realloc(g_fargc, sizeof (ctf_id_t) * nargs); + if (g_fargc == NULL) + ctfdump_fatal("failed to get memory for %d " + "ctf_id_t's\n", nargs); + g_nfargc = nargs; + } +} + +static int +ctfdump_functions_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *ctc, + void *arg) +{ + int i; + + if (ctc->ctc_argc != 0) { + ctfdump_fargs_grow(ctc->ctc_argc); + if (ctf_func_args(g_fp, symidx, g_nfargc, g_fargc) == CTF_ERR) + ctfdump_fatal("failed to get arguments for function " + "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp))); + } + + ctfdump_printf(CTFDUMP_FUNCTIONS, + " [%lu] %s (%lu) returns: %u args: (", g_stats.cs_nfuncs, name, + symidx, ctc->ctc_return); + for (i = 0; i < ctc->ctc_argc; i++) + ctfdump_printf(CTFDUMP_FUNCTIONS, "%lu%s", g_fargc[i], + i + 1 == ctc->ctc_argc ? "" : ", "); + if (ctc->ctc_flags & CTF_FUNC_VARARG) + ctfdump_printf(CTFDUMP_FUNCTIONS, "%s...", + ctc->ctc_argc == 0 ? "" : ", "); + ctfdump_printf(CTFDUMP_FUNCTIONS, ")\n"); + + g_stats.cs_nfuncs++; + g_stats.cs_nfuncargs += ctc->ctc_argc; + g_stats.cs_nfuncmax = MAX(ctc->ctc_argc, g_stats.cs_nfuncmax); + + return (0); +} + +static void +ctfdump_functions(void) +{ + ctfdump_title(CTFDUMP_FUNCTIONS, "Functions"); + + if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR) + ctfdump_fatal("failed to dump functions: %s\n", + ctf_errmsg(ctf_errno(g_fp))); +} + +static void +ctfdump_header(void) +{ + const ctf_header_t *hp; + const char *parname, *parlabel; + + ctfdump_title(CTFDUMP_HEADER, "CTF Header"); + ctf_dataptr(g_fp, (const void **)&hp, NULL); + ctfdump_printf(CTFDUMP_HEADER, " cth_magic = 0x%04x\n", + hp->cth_magic); + ctfdump_printf(CTFDUMP_HEADER, " cth_version = %u\n", + hp->cth_version); + ctfdump_printf(CTFDUMP_HEADER, " cth_flags = 0x%02x\n", + ctf_flags(g_fp)); + parname = ctf_parent_name(g_fp); + parlabel = ctf_parent_label(g_fp); + ctfdump_printf(CTFDUMP_HEADER, " cth_parlabel = %s\n", + parlabel == NULL ? "(anon)" : parlabel); + ctfdump_printf(CTFDUMP_HEADER, " cth_parname = %s\n", + parname == NULL ? "(anon)" : parname); + ctfdump_printf(CTFDUMP_HEADER, " cth_lbloff = %u\n", + hp->cth_lbloff); + ctfdump_printf(CTFDUMP_HEADER, " cth_objtoff = %u\n", + hp->cth_objtoff); + ctfdump_printf(CTFDUMP_HEADER, " cth_funcoff = %u\n", + hp->cth_funcoff); + ctfdump_printf(CTFDUMP_HEADER, " cth_typeoff = %u\n", + hp->cth_typeoff); + ctfdump_printf(CTFDUMP_HEADER, " cth_stroff = %u\n", + hp->cth_stroff); + ctfdump_printf(CTFDUMP_HEADER, " cth_strlen = %u\n", + hp->cth_strlen); +} + +static int +ctfdump_labels_cb(const char *name, const ctf_lblinfo_t *li, void *arg) +{ + ctfdump_printf(CTFDUMP_LABELS, " %5lu %s\n", li->ctb_typeidx, name); + return (0); +} + +static void +ctfdump_labels(void) +{ + ctfdump_title(CTFDUMP_LABELS, "Label Table"); + if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR) + ctfdump_fatal("failed to dump labels: %s\n", + ctf_errmsg(ctf_errno(g_fp))); +} + +static int +ctfdump_strings_cb(const char *s, void *arg) +{ + size_t len = strlen(s) + 1; + ulong_t *stroff = arg; + ctfdump_printf(CTFDUMP_STRINGS, " [%lu] %s\n", *stroff, + *s == '\0' ? "\\0" : s); + *stroff = *stroff + len; + g_stats.cs_nstrings++; + g_stats.cs_strsz += len; + g_stats.cs_strmax = MAX(g_stats.cs_strmax, len); + return (0); +} + +static void +ctfdump_strings(void) +{ + ulong_t stroff = 0; + + ctfdump_title(CTFDUMP_STRINGS, "String Table"); + if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR) + ctfdump_fatal("failed to dump strings: %s\n", + ctf_errmsg(ctf_errno(g_fp))); +} + +static void +ctfdump_stat_int(const char *name, ulong_t value) +{ + ctfdump_printf(CTFDUMP_STATS, " %-36s= %lu\n", name, value); +} + +static void +ctfdump_stat_fp(const char *name, float value) +{ + ctfdump_printf(CTFDUMP_STATS, " %-36s= %.2f\n", name, value); +} + +static void +ctfdump_stats(void) +{ + int i; + ulong_t sum; + + ctfdump_title(CTFDUMP_STATS, "CTF Statistics"); + + ctfdump_stat_int("total number of data objects", g_stats.cs_ndata); + ctfdump_printf(CTFDUMP_STATS, "\n"); + ctfdump_stat_int("total number of functions", g_stats.cs_nfuncs); + ctfdump_stat_int("total number of function arguments", + g_stats.cs_nfuncargs); + ctfdump_stat_int("maximum argument list length", g_stats.cs_nfuncmax); + if (g_stats.cs_nfuncs != 0) + ctfdump_stat_fp("average argument list length", + (float)g_stats.cs_nfuncargs / (float)g_stats.cs_nfuncs); + ctfdump_printf(CTFDUMP_STATS, "\n"); + + sum = 0; + for (i = 0; i < CTF_K_MAX; i++) + sum += g_stats.cs_ntypes[i]; + ctfdump_stat_int("total number of types", sum); + ctfdump_stat_int("total number of integers", + g_stats.cs_ntypes[CTF_K_INTEGER]); + ctfdump_stat_int("total number of floats", + g_stats.cs_ntypes[CTF_K_FLOAT]); + ctfdump_stat_int("total number of pointers", + g_stats.cs_ntypes[CTF_K_POINTER]); + ctfdump_stat_int("total number of arrays", + g_stats.cs_ntypes[CTF_K_ARRAY]); + ctfdump_stat_int("total number of func types", + g_stats.cs_ntypes[CTF_K_FUNCTION]); + ctfdump_stat_int("total number of structs", + g_stats.cs_ntypes[CTF_K_STRUCT]); + ctfdump_stat_int("total number of unions", + g_stats.cs_ntypes[CTF_K_UNION]); + ctfdump_stat_int("total number of enums", + g_stats.cs_ntypes[CTF_K_ENUM]); + ctfdump_stat_int("total number of forward tags", + g_stats.cs_ntypes[CTF_K_FORWARD]); + ctfdump_stat_int("total number of typedefs", + g_stats.cs_ntypes[CTF_K_TYPEDEF]); + ctfdump_stat_int("total number of volatile types", + g_stats.cs_ntypes[CTF_K_VOLATILE]); + ctfdump_stat_int("total number of const types", + g_stats.cs_ntypes[CTF_K_CONST]); + ctfdump_stat_int("total number of restrict types", + g_stats.cs_ntypes[CTF_K_RESTRICT]); + ctfdump_stat_int("total number of unknowns (holes)", + g_stats.cs_ntypes[CTF_K_UNKNOWN]); + + ctfdump_printf(CTFDUMP_STATS, "\n"); + ctfdump_stat_int("total number of struct members", g_stats.cs_nsmembs); + ctfdump_stat_int("maximum number of struct members", g_stats.cs_nsmax); + ctfdump_stat_int("total size of all structs", g_stats.cs_structsz); + ctfdump_stat_int("maximum size of a struct", g_stats.cs_sszmax); + if (g_stats.cs_ntypes[CTF_K_STRUCT] != 0) { + ctfdump_stat_fp("average number of struct members", + (float)g_stats.cs_nsmembs / + (float)g_stats.cs_ntypes[CTF_K_STRUCT]); + ctfdump_stat_fp("average size of a struct", + (float)g_stats.cs_structsz / + (float)g_stats.cs_ntypes[CTF_K_STRUCT]); + } + ctfdump_printf(CTFDUMP_STATS, "\n"); + ctfdump_stat_int("total number of union members", g_stats.cs_numembs); + ctfdump_stat_int("maximum number of union members", g_stats.cs_numax); + ctfdump_stat_int("total size of all unions", g_stats.cs_unionsz); + ctfdump_stat_int("maximum size of a union", g_stats.cs_uszmax); + if (g_stats.cs_ntypes[CTF_K_UNION] != 0) { + ctfdump_stat_fp("average number of union members", + (float)g_stats.cs_numembs / + (float)g_stats.cs_ntypes[CTF_K_UNION]); + ctfdump_stat_fp("average size of a union", + (float)g_stats.cs_unionsz / + (float)g_stats.cs_ntypes[CTF_K_UNION]); + } + ctfdump_printf(CTFDUMP_STATS, "\n"); + + ctfdump_stat_int("total number of enum members", g_stats.cs_nemembs); + ctfdump_stat_int("maximum number of enum members", g_stats.cs_nemax); + if (g_stats.cs_ntypes[CTF_K_ENUM] != 0) { + ctfdump_stat_fp("average number of enum members", + (float)g_stats.cs_nemembs / + (float)g_stats.cs_ntypes[CTF_K_ENUM]); + } + ctfdump_printf(CTFDUMP_STATS, "\n"); + + ctfdump_stat_int("total number of strings", g_stats.cs_nstrings); + ctfdump_stat_int("bytes of string data", g_stats.cs_strsz); + ctfdump_stat_int("maximum string length", g_stats.cs_strmax); + if (g_stats.cs_nstrings != 0) + ctfdump_stat_fp("average string length", + (float)g_stats.cs_strsz / (float)g_stats.cs_nstrings); + ctfdump_printf(CTFDUMP_STATS, "\n"); +} + +static void +ctfdump_intenc_name(ctf_encoding_t *cte, char *buf, int len) +{ + int off = 0; + boolean_t space = B_FALSE; + + if (cte->cte_format == 0 || (cte->cte_format & + ~(CTF_INT_SIGNED | CTF_INT_CHAR | CTF_INT_BOOL | + CTF_INT_VARARGS)) != 0) { + (void) snprintf(buf, len, "0x%x", cte->cte_format); + return; + } + + if (cte->cte_format & CTF_INT_SIGNED) { + off += snprintf(buf + off, MAX(len - off, 0), "%sSIGNED", + space == B_TRUE ? " " : ""); + space = B_TRUE; + } + + if (cte->cte_format & CTF_INT_CHAR) { + off += snprintf(buf + off, MAX(len - off, 0), "%sCHAR", + space == B_TRUE ? " " : ""); + space = B_TRUE; + } + + if (cte->cte_format & CTF_INT_BOOL) { + off += snprintf(buf + off, MAX(len - off, 0), "%sBOOL", + space == B_TRUE ? " " : ""); + space = B_TRUE; + } + + if (cte->cte_format & CTF_INT_VARARGS) { + off += snprintf(buf + off, MAX(len - off, 0), "%sVARARGS", + space == B_TRUE ? " " : ""); + space = B_TRUE; + } +} + +static int +ctfdump_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg) +{ + int *count = arg; + ctfdump_printf(CTFDUMP_TYPES, "\t%s type=%lu off=%lu\n", member, type, + off); + *count = *count + 1; + return (0); +} + +static int +ctfdump_enum_cb(const char *name, int value, void *arg) +{ + int *count = arg; + ctfdump_printf(CTFDUMP_TYPES, "\t%s = %d\n", name, value); + *count = *count + 1; + return (0); +} + +static int +ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg) +{ + int kind, i, count; + ctf_id_t ref; + char name[512], ienc[128]; + const char *encn; + ctf_funcinfo_t ctc; + ctf_arinfo_t ar; + ctf_encoding_t cte; + ssize_t size; + + if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR) + ctfdump_fatal("encountered malformed ctf, type %s does not " + "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp))); + + if (ctf_type_name(g_fp, id, name, sizeof (name)) == NULL) { + if (ctf_errno(g_fp) != ECTF_NOPARENT) + ctfdump_fatal("type %lu missing name: %s\n", id, + ctf_errmsg(ctf_errno(g_fp))); + (void) snprintf(name, sizeof (name), "(unknown %s)", + ctfdump_knames[kind]); + } + + g_stats.cs_ntypes[kind]++; + if (root == B_TRUE) + ctfdump_printf(CTFDUMP_TYPES, " <%lu> ", id); + else + ctfdump_printf(CTFDUMP_TYPES, " [%lu] ", id); + + switch (kind) { + case CTF_K_UNKNOWN: + break; + case CTF_K_INTEGER: + if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR) + ctfdump_fatal("failed to get encoding information " + "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_intenc_name(&cte, ienc, sizeof (ienc)); + ctfdump_printf(CTFDUMP_TYPES, + "%s encoding=%s offset=%u bits=%u", + name, ienc, cte.cte_offset, cte.cte_bits); + break; + case CTF_K_FLOAT: + if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR) + ctfdump_fatal("failed to get encoding information " + "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp))); + if (cte.cte_format < 1 || cte.cte_format > 12) + encn = "unknown"; + else + encn = ctfdump_fpenc[cte.cte_format]; + ctfdump_printf(CTFDUMP_TYPES, "%s encoding=%s offset=%u " + "bits=%u", name, encn, cte.cte_offset, cte.cte_bits); + break; + case CTF_K_POINTER: + if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) + ctfdump_fatal("failed to get reference type for %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name, + ref); + break; + case CTF_K_ARRAY: + if (ctf_array_info(g_fp, id, &ar) == CTF_ERR) + ctfdump_fatal("failed to get array information for " + "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "%s contents: %lu, index: %lu", + name, ar.ctr_contents, ar.ctr_index); + break; + case CTF_K_FUNCTION: + if (ctf_func_info_by_id(g_fp, id, &ctc) == CTF_ERR) + ctfdump_fatal("failed to get function info for %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + if (ctc.ctc_argc > 0) { + ctfdump_fargs_grow(ctc.ctc_argc); + if (ctf_func_args_by_id(g_fp, id, g_nfargc, g_fargc) == + CTF_ERR) + ctfdump_fatal("failed to get function " + "arguments for %s: %s\n", name, + ctf_errmsg(ctf_errno(g_fp))); + } + ctfdump_printf(CTFDUMP_TYPES, + "%s returns: %lu args: (", name, ctc.ctc_return); + for (i = 0; i < ctc.ctc_argc; i++) { + ctfdump_printf(CTFDUMP_TYPES, "%lu%s", g_fargc[i], + i + 1 == ctc.ctc_argc ? "" : ", "); + } + if (ctc.ctc_flags & CTF_FUNC_VARARG) + ctfdump_printf(CTFDUMP_TYPES, "%s...", + ctc.ctc_argc == 0 ? "" : ", "); + ctfdump_printf(CTFDUMP_TYPES, ")"); + break; + case CTF_K_STRUCT: + case CTF_K_UNION: + size = ctf_type_size(g_fp, id); + if (size == CTF_ERR) + ctfdump_fatal("failed to get size of %s: %s\n", name, + ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "%s (%d bytes)\n", name, size); + count = 0; + if (ctf_member_iter(g_fp, id, ctfdump_member_cb, &count) != 0) + ctfdump_fatal("failed to iterate members of %s: %s\n", + name, ctf_errmsg(ctf_errno(g_fp))); + if (kind == CTF_K_STRUCT) { + g_stats.cs_nsmembs += count; + g_stats.cs_nsmax = MAX(count, g_stats.cs_nsmax); + g_stats.cs_structsz += size; + g_stats.cs_sszmax = MAX(size, g_stats.cs_sszmax); + } else { + g_stats.cs_numembs += count; + g_stats.cs_numax = MAX(count, g_stats.cs_numax); + g_stats.cs_unionsz += size; + g_stats.cs_uszmax = MAX(count, g_stats.cs_uszmax); + } + break; + case CTF_K_ENUM: + ctfdump_printf(CTFDUMP_TYPES, "%s\n", name); + count = 0; + if (ctf_enum_iter(g_fp, id, ctfdump_enum_cb, &count) != 0) + ctfdump_fatal("failed to iterate enumerators of %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + g_stats.cs_nemembs += count; + g_stats.cs_nemax = MAX(g_stats.cs_nemax, count); + break; + case CTF_K_FORWARD: + ctfdump_printf(CTFDUMP_TYPES, "forward %s\n", name); + break; + case CTF_K_TYPEDEF: + if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) + ctfdump_fatal("failed to get reference type for %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "typedef %s refers to %lu", name, + ref); + break; + case CTF_K_VOLATILE: + if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) + ctfdump_fatal("failed to get reference type for %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name, + ref); + break; + case CTF_K_CONST: + if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) + ctfdump_fatal("failed to get reference type for %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name, + ref); + break; + case CTF_K_RESTRICT: + if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR) + ctfdump_fatal("failed to get reference type for %s: " + "%s\n", name, ctf_errmsg(ctf_errno(g_fp))); + ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name, + ref); + break; + default: + ctfdump_fatal("encountered unknown kind for type %s: %d\n", + name, kind); + } + + ctfdump_printf(CTFDUMP_TYPES, "\n"); + + return (0); +} + +static void +ctfdump_types(void) +{ + ctfdump_title(CTFDUMP_TYPES, "Types"); + + if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR) + ctfdump_fatal("failed to dump labels: %s\n", + ctf_errmsg(ctf_errno(g_fp))); +} + +static void +ctfdump_output(const char *out) +{ + int fd, ret; + const void *data; + size_t len; + + ctf_dataptr(g_fp, &data, &len); + if ((fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) + ctfdump_fatal("failed to open output file %s: %s\n", out, + strerror(errno)); + + while (len > 0) { + ret = write(fd, data, len); + if (ret == -1 && errno == EINTR) + continue; + else if (ret == -1 && (errno == EFAULT || errno == EBADF)) + abort(); + else if (ret == -1) + ctfdump_fatal("failed to write to %s: %s\n", out, + strerror(errno)); + data += ret; + len -= ret; + } + + do { + ret = close(fd); + } while (ret == -1 && errno == EINTR); + if (ret != 0 && errno == EBADF) + abort(); + if (ret != 0) + ctfdump_fatal("failed to close %s: %s\n", out, strerror(errno)); +} + +int +main(int argc, char *argv[]) +{ + int c, fd, err; + const char *ufile = NULL, *parent = NULL; + + g_progname = basename(argv[0]); + while ((c = getopt(argc, argv, ":dfhlp:sStu:")) != -1) { + switch (c) { + case 'd': + g_dump |= CTFDUMP_OBJECTS; + break; + case 'f': + g_dump |= CTFDUMP_FUNCTIONS; + break; + case 'h': + g_dump |= CTFDUMP_HEADER; + break; + case 'l': + g_dump |= CTFDUMP_LABELS; + break; + case 'p': + parent = optarg; + break; + case 's': + g_dump |= CTFDUMP_STRINGS; + break; + case 'S': + g_dump |= CTFDUMP_STATS; + break; + case 't': + g_dump |= CTFDUMP_TYPES; + break; + case 'u': + g_dump |= CTFDUMP_OUTPUT; + ufile = optarg; + break; + case '?': + ctfdump_usage("Option -%c requires an operand\n", + optopt); + return (2); + case ':': + ctfdump_usage("Unknown option: -%c\n", optopt); + return (2); + } + } + + argc -= optind; + argv += optind; + + /* + * Dump all information by default. + */ + if (g_dump == 0) + g_dump = CTFDUMP_DEFAULT; + + if (argc != 1) { + ctfdump_usage("no file to dump\n"); + return (2); + } + + if ((fd = open(argv[0], O_RDONLY)) < 0) + ctfdump_fatal("failed to open file %s: %s\n", argv[0], + strerror(errno)); + + g_fp = ctf_fdopen(fd, &err); + if (g_fp == NULL) + ctfdump_fatal("failed to open file %s: %s\n", argv[0], + ctf_errmsg(err)); + + if (parent != NULL) { + ctf_file_t *pfp = ctf_open(parent, &err); + + if (pfp == NULL) + ctfdump_fatal("failed to open parent file %s: %s\n", + parent, ctf_errmsg(err)); + if (ctf_import(g_fp, pfp) != 0) + ctfdump_fatal("failed to import parent %s: %s\n", + parent, ctf_errmsg(ctf_errno(g_fp))); + } + + /* + * If stats is set, we must run through everything exect CTFDUMP_OUTPUT. + * We also do CTFDUMP_STATS last as a result. + */ + if (g_dump & CTFDUMP_HEADER) + ctfdump_header(); + + if (g_dump & (CTFDUMP_LABELS | CTFDUMP_STATS)) + ctfdump_labels(); + + if (g_dump & (CTFDUMP_OBJECTS | CTFDUMP_STATS)) + ctfdump_objects(); + + if (g_dump & (CTFDUMP_FUNCTIONS | CTFDUMP_STATS)) + ctfdump_functions(); + + if (g_dump & (CTFDUMP_TYPES | CTFDUMP_STATS)) + ctfdump_types(); + + if (g_dump & (CTFDUMP_STRINGS | CTFDUMP_STATS)) + ctfdump_strings(); + + if (g_dump & CTFDUMP_STATS) + ctfdump_stats(); + + if (g_dump & CTFDUMP_OUTPUT) + ctfdump_output(ufile); + + return (0); +} diff --git a/usr/src/cmd/ctfmerge/Makefile b/usr/src/cmd/ctfmerge/Makefile new file mode 100644 index 0000000000..02ead98687 --- /dev/null +++ b/usr/src/cmd/ctfmerge/Makefile @@ -0,0 +1,33 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2015, Joyent, Inc. +# + +PROG= ctfmerge + +include ../Makefile.cmd + +CFLAGS += $(CCVERBOSE) +LDLIBS += -lctf -lelf + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/ctfmerge/ctfmerge.c b/usr/src/cmd/ctfmerge/ctfmerge.c new file mode 100644 index 0000000000..a64936ccbe --- /dev/null +++ b/usr/src/cmd/ctfmerge/ctfmerge.c @@ -0,0 +1,487 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015, Joyent, Inc. + */ + +/* + * merge CTF containers + */ + +#include <stdio.h> +#include <libctf.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <unistd.h> +#include <sys/fcntl.h> +#include <stdlib.h> +#include <libelf.h> +#include <gelf.h> +#include <sys/mman.h> +#include <libgen.h> +#include <stdarg.h> + +static char *g_progname; +static char *g_unique; +static char *g_outfile; +static boolean_t g_req; +static uint_t g_nctf; + +static void +ctfmerge_fatal(const char *fmt, ...) +{ + va_list ap; + + (void) fprintf(stderr, "%s: ", g_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + + if (g_outfile != NULL) + (void) unlink(g_outfile); + + exit(1); +} + +static boolean_t +ctfmerge_expect_ctf(const char *name, Elf *elf) +{ + Elf_Scn *scn, *strscn; + Elf_Data *data, *strdata; + GElf_Shdr shdr; + ulong_t i; + + if (g_req == B_FALSE) + return (B_FALSE); + + scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) == NULL) { + ctfmerge_fatal("failed to get section header for file " + "%s: %s\n", name, elf_errmsg(elf_errno())); + } + + if (shdr.sh_type == SHT_SYMTAB) + break; + } + + if (scn == NULL) + return (B_FALSE); + + if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) + ctfmerge_fatal("failed to get section header for file %s: %s\n", + name, elf_errmsg(elf_errno())); + + if ((data = elf_getdata(scn, NULL)) == NULL) + ctfmerge_fatal("failed to read symbol table for %s: %s\n", + name, elf_errmsg(elf_errno())); + + if ((strdata = elf_getdata(strscn, NULL)) == NULL) + ctfmerge_fatal("failed to read string table for %s: %s\n", + name, elf_errmsg(elf_errno())); + + for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + GElf_Sym sym; + const char *file; + size_t len; + + if (gelf_getsym(data, i, &sym) == NULL) + ctfmerge_fatal("failed to read symbol table entry %d " + "for %s: %s\n", i, name, elf_errmsg(elf_errno())); + + if (GELF_ST_TYPE(sym.st_info) != STT_FILE) + continue; + + file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name); + len = strlen(file); + if (len < 2 || name[len - 2] != '.') + continue; + + if (name[len - 1] == 'c') + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * Go through and construct enough information for this Elf Object to try and do + * a ctf_bufopen(). + */ +static void +ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh) +{ + GElf_Ehdr ehdr; + GElf_Shdr shdr; + Elf_Scn *scn; + Elf_Data *ctf_data, *str_data, *sym_data; + ctf_sect_t ctfsect, symsect, strsect; + ctf_file_t *fp; + int err; + + if (gelf_getehdr(elf, &ehdr) == NULL) + ctfmerge_fatal("failed to get ELF header for %s: %s\n", + name, elf_errmsg(elf_errno())); + + bzero(&ctfsect, sizeof (ctf_sect_t)); + bzero(&symsect, sizeof (ctf_sect_t)); + bzero(&strsect, sizeof (ctf_sect_t)); + + scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) { + const char *sname; + + if (gelf_getshdr(scn, &shdr) == NULL) + ctfmerge_fatal("failed to get section header for " + "file %s: %s\n", name, elf_errmsg(elf_errno())); + + sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name); + if (shdr.sh_type == SHT_PROGBITS && + strcmp(sname, ".SUNW_ctf") == 0) { + ctfsect.cts_name = sname; + ctfsect.cts_type = shdr.sh_type; + ctfsect.cts_flags = shdr.sh_flags; + ctfsect.cts_size = shdr.sh_size; + ctfsect.cts_entsize = shdr.sh_entsize; + ctfsect.cts_offset = (off64_t)shdr.sh_offset; + + ctf_data = elf_getdata(scn, NULL); + if (ctf_data == NULL) + ctfmerge_fatal("failed to get ELF CTF " + "data section for %s: %s\n", name, + elf_errmsg(elf_errno())); + ctfsect.cts_data = ctf_data->d_buf; + } else if (shdr.sh_type == SHT_SYMTAB) { + Elf_Scn *strscn; + GElf_Shdr strhdr; + + symsect.cts_name = sname; + symsect.cts_type = shdr.sh_type; + symsect.cts_flags = shdr.sh_flags; + symsect.cts_size = shdr.sh_size; + symsect.cts_entsize = shdr.sh_entsize; + symsect.cts_offset = (off64_t)shdr.sh_offset; + + if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL || + gelf_getshdr(strscn, &strhdr) == NULL) + ctfmerge_fatal("failed to get " + "string table for file %s: %s\n", name, + elf_errmsg(elf_errno())); + + strsect.cts_name = elf_strptr(elf, ehdr.e_shstrndx, + strhdr.sh_name); + strsect.cts_type = strhdr.sh_type; + strsect.cts_flags = strhdr.sh_flags; + strsect.cts_size = strhdr.sh_size; + strsect.cts_entsize = strhdr.sh_entsize; + strsect.cts_offset = (off64_t)strhdr.sh_offset; + + sym_data = elf_getdata(scn, NULL); + if (sym_data == NULL) + ctfmerge_fatal("failed to get ELF CTF " + "data section for %s: %s\n", name, + elf_errmsg(elf_errno())); + symsect.cts_data = sym_data->d_buf; + + str_data = elf_getdata(strscn, NULL); + if (str_data == NULL) + ctfmerge_fatal("failed to get ELF CTF " + "data section for %s: %s\n", name, + elf_errmsg(elf_errno())); + strsect.cts_data = str_data->d_buf; + } + } + + if (ctfsect.cts_type == SHT_NULL) { + if (ctfmerge_expect_ctf(name, elf) == B_FALSE) + return; + ctfmerge_fatal("failed to open %s: %s\n", name, + ctf_errmsg(ECTF_NOCTFDATA)); + } + + if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) { + fp = ctf_bufopen(&ctfsect, &symsect, &strsect, &err); + } else { + fp = ctf_bufopen(&ctfsect, NULL, NULL, &err); + } + + if (fp == NULL) { + if (ctfmerge_expect_ctf(name, elf) == B_TRUE) { + ctfmerge_fatal("failed to open file %s: %s\n", + name, ctf_errmsg(err)); + } + } else { + if (ctf_merge_add(cmh, fp) != 0) { + ctfmerge_fatal("failed to add input %s: %s\n", + name, ctf_errmsg(ctf_errno(fp))); + exit(1); + } + g_nctf++; + } +} + +static void +ctfmerge_read_archive(const char *name, int fd, Elf *elf, + ctf_merge_t *cmh) +{ + Elf *aelf; + Elf_Cmd cmd = ELF_C_READ; + int cursec = 1; + char *nname; + + while ((aelf = elf_begin(fd, cmd, elf)) != NULL) { + Elf_Arhdr *arhdr; + boolean_t leakelf = B_FALSE; + + if ((arhdr = elf_getarhdr(aelf)) == NULL) + ctfmerge_fatal("failed to get archive header %d for " + "%s: %s\n", cursec, name, elf_errmsg(elf_errno())); + + if (*(arhdr->ar_name) == '/') + goto next; + + if (asprintf(&nname, "%s.%s.%d", name, arhdr->ar_name, + cursec) < 0) + ctfmerge_fatal("failed to allocate memory for archive " + "%d of file %s\n", cursec, name); + + switch (elf_kind(aelf)) { + case ELF_K_AR: + ctfmerge_read_archive(nname, fd, aelf, cmh); + free(nname); + break; + case ELF_K_ELF: + ctfmerge_elfopen(nname, aelf, cmh); + free(nname); + leakelf = B_TRUE; + break; + default: + ctfmerge_fatal("unknown elf kind (%d) in archive %d " + "for %s\n", elf_kind(aelf), cursec, name); + } + +next: + cmd = elf_next(aelf); + if (leakelf == B_FALSE) + (void) elf_end(aelf); + cursec++; + } +} + +static void +ctfmerge_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", g_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-gt] [-d uniqfile] [-l label] " + "[-L labelenv] -o outfile file ...\n" + "\n" + "\t-d uniquify merged output against uniqfile\n" + "\t-g do not remove source debug information (STABS, DWARF)\n" + "\t-l set output container's label to specified value\n" + "\t-L set output container's label to value from environment\n" + "\t-o file to add CTF data to\n" + "\t-t require CTF data from all inputs built from C sources\n", + g_progname); +} + +int +main(int argc, char *argv[]) +{ + int err, i, c, ofd; + char *tmpfile = NULL, *label = NULL; + int wflags = CTF_ELFWRITE_F_COMPRESS; + ctf_file_t *ofp; + ctf_merge_t *cmh; + + g_progname = basename(argv[0]); + + /* + * We support a subset of the old CTF merge flags, mostly for + * compatability. + */ + while ((c = getopt(argc, argv, ":d:fgL:o:t")) != -1) { + switch (c) { + case 'd': + g_unique = optarg; + break; + case 'f': + /* Silently ignored for compatibility */ + break; + case 'g': + /* Silently ignored for compatibility */ + break; + case 'l': + label = optarg; + break; + case 'L': + label = getenv(optarg); + break; + case 'o': + g_outfile = optarg; + break; + case 't': + g_req = B_TRUE; + break; + case ':': + ctfmerge_usage("ctfmerge: Option -%c requires an " + "operand\n", optopt); + return (2); + case '?': + ctfmerge_usage("Unknown option: -%c\n", optopt); + return (2); + } + } + + if (g_outfile == NULL) { + ctfmerge_usage("missing required -o output file\n"); + return (2); + } + + (void) elf_version(EV_CURRENT); + + /* + * Obviously this isn't atomic, but at least gives us a good starting + * point. + */ + if ((ofd = open(g_outfile, O_RDWR)) < 0) + ctfmerge_fatal("cannot open output file %s: %s\n", g_outfile, + strerror(errno)); + + argc -= optind; + argv += optind; + + if (argc < 1) { + ctfmerge_usage("no input files specified"); + return (2); + } + + cmh = ctf_merge_init(ofd, &err); + if (cmh == NULL) + ctfmerge_fatal("failed to create merge handle: %s\n", + ctf_errmsg(err)); + + for (i = 0; i < argc; i++) { + ctf_file_t *ifp; + int fd; + + if ((fd = open(argv[i], O_RDONLY)) < 0) + ctfmerge_fatal("failed to open file %s: %s\n", + argv[i], strerror(errno)); + ifp = ctf_fdopen(fd, &err); + if (ifp == NULL) { + Elf *e; + + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + (void) close(fd); + ctfmerge_fatal("failed to open %s: %s\n", + argv[i], ctf_errmsg(err)); + } + + /* + * It's an ELF file, check if we have an archive or if + * we're expecting CTF here. + */ + switch (elf_kind(e)) { + case ELF_K_AR: + break; + case ELF_K_ELF: + if (ctfmerge_expect_ctf(argv[i], e) == B_TRUE) { + (void) elf_end(e); + (void) close(fd); + ctfmerge_fatal("failed to " + "open %s: file was built from C " + "sources, but missing CTF\n", + argv[i]); + } + (void) elf_end(e); + (void) close(fd); + continue; + default: + (void) elf_end(e); + (void) close(fd); + ctfmerge_fatal("failed to open %s: " + "unsupported ELF file type", argv[i]); + } + + ctfmerge_read_archive(argv[i], fd, e, cmh); + (void) elf_end(e); + (void) close(fd); + continue; + } + (void) close(fd); + if ((err = ctf_merge_add(cmh, ifp)) != 0) + ctfmerge_fatal("failed to add input %s: %s\n", + argv[i], ctf_errmsg(err)); + g_nctf++; + } + + if (g_nctf == 0) { + ctf_merge_fini(cmh); + return (0); + } + + if (g_unique != NULL) { + ctf_file_t *ufp; + char *base; + + ufp = ctf_open(g_unique, &err); + if (ufp == NULL) { + ctfmerge_fatal("failed to open uniquify file %s: %s\n", + g_unique, ctf_errmsg(err)); + return (1); + } + + base = basename(g_unique); + (void) ctf_merge_uniquify(cmh, ufp, base); + } + + if (label != NULL) { + if ((err = ctf_merge_label(cmh, label)) != 0) + ctfmerge_fatal("failed to add label %s: %s\n", label, + ctf_errmsg(err)); + } + + err = ctf_merge_merge(cmh, &ofp); + if (err != 0) + ctfmerge_fatal("failed to merge types: %s\n", ctf_errmsg(err)); + ctf_merge_fini(cmh); + + if (asprintf(&tmpfile, "%s.ctf", g_outfile) == -1) + ctfmerge_fatal("ran out of memory for temporary file name\n"); + err = ctf_elfwrite(ofp, g_outfile, tmpfile, wflags); + free(tmpfile); + if (err == CTF_ERR) { + (void) unlink(tmpfile); + ctfmerge_fatal("encountered a libctf error: %s!\n", + ctf_errmsg(ctf_errno(ofp))); + } + + if (rename(tmpfile, g_outfile) != 0) { + (void) unlink(tmpfile); + ctfmerge_fatal("failed to rename temporary file: %s\n", + strerror(errno)); + } + + return (0); +} diff --git a/usr/src/cmd/devfsadm/Makefile.com b/usr/src/cmd/devfsadm/Makefile.com index 4df3b00585..1585db2894 100644 --- a/usr/src/cmd/devfsadm/Makefile.com +++ b/usr/src/cmd/devfsadm/Makefile.com @@ -67,7 +67,6 @@ LINK_OBJS_CMN = \ fssnap_link.o \ sgen_link.o \ smp_link.o \ - md_link.o \ dtrace_link.o \ vscan_link.o \ zfs_link.o \ diff --git a/usr/src/cmd/devfsadm/devlink.tab.sh b/usr/src/cmd/devfsadm/devlink.tab.sh index 6724fcb573..0267efeb9f 100644 --- a/usr/src/cmd/devfsadm/devlink.tab.sh +++ b/usr/src/cmd/devfsadm/devlink.tab.sh @@ -22,8 +22,7 @@ # # Copyright (c) 1998, 2000 by Sun Microsystems, Inc. # All rights reserved. -# -#ident "%Z%%M% %I% %E% SMI" +# Copyright (c) 2012 Joyent, Inc. All rights reserved. # # This is the script that generates the devlink.tab file. It is # architecture-aware, and dumps different stuff for x86 and sparc. @@ -34,8 +33,6 @@ # cat <<EOM -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1998 by Sun Microsystems, Inc. # # diff --git a/usr/src/cmd/devfsadm/i386/Makefile b/usr/src/cmd/devfsadm/i386/Makefile index 1f14c93dad..75f2da3436 100644 --- a/usr/src/cmd/devfsadm/i386/Makefile +++ b/usr/src/cmd/devfsadm/i386/Makefile @@ -24,8 +24,11 @@ LINK_OBJS_i386 = \ misc_link_i386.o \ + lx_link_i386.o \ xen_link.o +lx_link_i386.o lx_link_i386.po lx_link_i386.ln := CPPFLAGS += -I$(UTSBASE)/common/brand/lx + xen_link.o xen_link.ln xen_link.po := CPPFLAGS += -I$(UTSBASE)/i86xpv include ../Makefile.com diff --git a/usr/src/cmd/devfsadm/i386/lx_link_i386.c b/usr/src/cmd/devfsadm/i386/lx_link_i386.c new file mode 100644 index 0000000000..855f4f7383 --- /dev/null +++ b/usr/src/cmd/devfsadm/i386/lx_link_i386.c @@ -0,0 +1,86 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <devfsadm.h> +#include <strings.h> +#include <stdio.h> +#include <sys/lx_ptm.h> +#include <sys/lx_audio.h> + +static int lx_ptm(di_minor_t minor, di_node_t node); +static int lx_audio(di_minor_t minor, di_node_t node); +static int lx_systrace(di_minor_t minor, di_node_t node); + +static devfsadm_create_t lx_create_cbt[] = { + { "pseudo", "ddi_pseudo", LX_PTM_DRV, + TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_ptm }, + { "pseudo", "ddi_pseudo", LX_AUDIO_DRV, + TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_audio }, + { "pseudo", "ddi_pseudo", "lx_systrace", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_systrace }, +}; + +DEVFSADM_CREATE_INIT_V0(lx_create_cbt); + +static int +lx_ptm(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + + if (strcmp(LX_PTM_MINOR_NODE, mname) == 0) + (void) devfsadm_mklink("brand/lx/ptmx", node, minor, 0); + + return (DEVFSADM_CONTINUE); +} + +static int +lx_audio(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + + if (strcmp(LXA_MINORNAME_DEVCTL, mname) == 0) + (void) devfsadm_mklink("brand/lx/audio_devctl", node, minor, 0); + if (strcmp(LXA_MINORNAME_DSP, mname) == 0) + (void) devfsadm_mklink("brand/lx/dsp", node, minor, 0); + if (strcmp(LXA_MINORNAME_MIXER, mname) == 0) + (void) devfsadm_mklink("brand/lx/mixer", node, minor, 0); + + return (DEVFSADM_CONTINUE); +} + +static int +lx_systrace(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), "dtrace/provider/%s", mname); + (void) devfsadm_mklink(path, node, minor, 0); + + return (DEVFSADM_CONTINUE); +} diff --git a/usr/src/cmd/devfsadm/misc_link.c b/usr/src/cmd/devfsadm/misc_link.c index b7aef8b00d..fa55eb401f 100644 --- a/usr/src/cmd/devfsadm/misc_link.c +++ b/usr/src/cmd/devfsadm/misc_link.c @@ -21,7 +21,7 @@ /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. */ #include <regex.h> @@ -32,6 +32,7 @@ #include <limits.h> #include <sys/zone.h> #include <sys/zcons.h> +#include <sys/zfd.h> #include <sys/cpuid_drv.h> static int display(di_minor_t minor, di_node_t node); @@ -53,6 +54,7 @@ static int av_create(di_minor_t minor, di_node_t node); static int tsalarm_create(di_minor_t minor, di_node_t node); static int ntwdt_create(di_minor_t minor, di_node_t node); static int zcons_create(di_minor_t minor, di_node_t node); +static int zfd_create(di_minor_t minor, di_node_t node); static int cpuid(di_minor_t minor, di_node_t node); static int glvc(di_minor_t minor, di_node_t node); static int ses_callback(di_minor_t minor, di_node_t node); @@ -89,6 +91,9 @@ static devfsadm_create_t misc_cbt[] = { { "pseudo", "ddi_pseudo", "consms", TYPE_EXACT | DRV_EXACT, ILEVEL_0, consms }, + { "pseudo", "ddi_pseudo", "eventfd", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name + }, { "pseudo", "ddi_pseudo", "rsm", TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name }, @@ -108,6 +113,9 @@ static devfsadm_create_t misc_cbt[] = { "(^nca$)|(^rds$)|(^sdp$)|(^ipnet$)|(^dlpistub$)|(^bpf$)", TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name }, + { "pseudo", "ddi_pseudo", "inotify", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name + }, { "pseudo", "ddi_pseudo", "ipd", TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name }, @@ -123,6 +131,9 @@ static devfsadm_create_t misc_cbt[] = { "(^kdmouse$)|(^rootprop$)", TYPE_EXACT | DRV_RE, ILEVEL_0, node_name }, + { "pseudo", "ddi_pseudo", "timerfd", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name + }, { "pseudo", "ddi_pseudo", "tod", TYPE_EXACT | DRV_EXACT, ILEVEL_0, node_name }, @@ -171,6 +182,9 @@ static devfsadm_create_t misc_cbt[] = { { "pseudo", "ddi_pseudo", "zcons", TYPE_EXACT | DRV_EXACT, ILEVEL_0, zcons_create, }, + { "pseudo", "ddi_pseudo", "zfd", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, zfd_create, + }, { "pseudo", "ddi_pseudo", CPUID_DRIVER_NAME, TYPE_EXACT | DRV_EXACT, ILEVEL_0, cpuid, }, @@ -195,6 +209,9 @@ static devfsadm_create_t misc_cbt[] = { { "pseudo", "ddi_pseudo", "tpm", TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name }, + { "pseudo", "ddi_pseudo", "overlay", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name + } }; DEVFSADM_CREATE_INIT_V0(misc_cbt); @@ -219,6 +236,9 @@ static devfsadm_remove_t misc_remove_cbt[] = { ZCONS_SLAVE_NAME ")$", RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all }, + { "pseudo", "^zfd/" ZONENAME_REGEXP "/(master|slave)/[0-9]+$", + RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all + }, { "pseudo", "^" CPUID_SELF_NAME "$", RM_ALWAYS | RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all }, @@ -666,6 +686,35 @@ zcons_create(di_minor_t minor, di_node_t node) return (DEVFSADM_CONTINUE); } +static int +zfd_create(di_minor_t minor, di_node_t node) +{ + char *minor_str; + char *zonename; + int *id; + char path[MAXPATHLEN]; + + minor_str = di_minor_name(minor); + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname", + &zonename) == -1) + return (DEVFSADM_CONTINUE); + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "zfd_id", &id) == -1) + return (DEVFSADM_CONTINUE); + + if (strncmp(minor_str, "slave", 5) == 0) { + (void) snprintf(path, sizeof (path), "zfd/%s/slave/%d", + zonename, id[0]); + } else { + (void) snprintf(path, sizeof (path), "zfd/%s/master/%d", + zonename, id[0]); + } + (void) devfsadm_mklink(path, node, minor, 0); + + return (DEVFSADM_CONTINUE); +} + /* * /dev/cpu/self/cpuid -> /devices/pseudo/cpuid@0:self */ diff --git a/usr/src/cmd/dladm/Makefile b/usr/src/cmd/dladm/Makefile index 143086e26a..1f07bbd714 100644 --- a/usr/src/cmd/dladm/Makefile +++ b/usr/src/cmd/dladm/Makefile @@ -21,6 +21,7 @@ # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2015 Joyent, Inc. # PROG= dladm @@ -35,7 +36,7 @@ include ../Makefile.cmd XGETFLAGS += -a -x $(PROG).xcl LDLIBS += -L$(ROOT)/lib -lsocket LDLIBS += -ldladm -ldlpi -lkstat -lsecdb -lbsm -linetutil -ldevinfo -LDLIBS += $(ZLAZYLOAD) -lrstp $(ZNOLAZYLOAD) +LDLIBS += $(ZLAZYLOAD) -lrstp $(ZNOLAZYLOAD) -lnsl -lumem -lcmdutils CERRWARN += -_gcc=-Wno-switch CERRWARN += -_gcc=-Wno-unused-label diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index 904eca536d..8e4f61fae0 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015 Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -59,6 +60,7 @@ #include <libdliptun.h> #include <libdlsim.h> #include <libdlbridge.h> +#include <libdloverlay.h> #include <libinetutil.h> #include <libvrrpadm.h> #include <bsm/adt.h> @@ -74,6 +76,7 @@ #include <stddef.h> #include <stp_in.h> #include <ofmt.h> +#include <libcmdutils.h> #define MAXPORT 256 #define MAXVNIC 256 @@ -154,6 +157,7 @@ typedef struct show_vnic_state { dladm_status_t vs_status; uint32_t vs_flags; ofmt_handle_t vs_ofmt; + char *vs_zonename; } show_vnic_state_t; typedef struct show_part_state { @@ -192,6 +196,7 @@ static ofmt_cb_t print_lacp_cb, print_phys_one_mac_cb; static ofmt_cb_t print_xaggr_cb, print_aggr_stats_cb; static ofmt_cb_t print_phys_one_hwgrp_cb, print_wlan_attr_cb; static ofmt_cb_t print_wifi_status_cb, print_link_attr_cb; +static ofmt_cb_t print_overlay_cb, print_overlay_fma_cb, print_overlay_targ_cb; static void dladm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t); typedef void cmdfunc_t(int, char **, const char *); @@ -219,6 +224,8 @@ static cmdfunc_t do_create_bridge, do_modify_bridge, do_delete_bridge; static cmdfunc_t do_add_bridge, do_remove_bridge, do_show_bridge; static cmdfunc_t do_create_iptun, do_modify_iptun, do_delete_iptun; static cmdfunc_t do_show_iptun, do_up_iptun, do_down_iptun; +static cmdfunc_t do_create_overlay, do_delete_overlay, do_modify_overlay; +static cmdfunc_t do_show_overlay; static void do_up_vnic_common(int, char **, const char *, boolean_t); @@ -254,8 +261,11 @@ static void die(const char *, ...); static void die_optdup(int); static void die_opterr(int, int, const char *); static void die_dlerr(dladm_status_t, const char *, ...); +static void die_dlerrlist(dladm_status_t, dladm_errlist_t *, + const char *, ...); static void warn(const char *, ...); static void warn_dlerr(dladm_status_t, const char *, ...); +static void warn_dlerrlist(dladm_errlist_t *); typedef struct cmd { char *c_name; @@ -265,7 +275,7 @@ typedef struct cmd { static cmd_t cmds[] = { { "rename-link", do_rename_link, - " rename-link <oldlink> <newlink>" }, + " rename-link [-z zonename] <oldlink> <newlink>" }, { "show-link", do_show_link, " show-link [-pP] [-o <field>,..] [-s [-i <interval>]] " "[<link>]\n" }, @@ -300,12 +310,13 @@ static cmd_t cmds[] = { { "show-wifi", do_show_wifi, " show-wifi [-p] [-o <field>,...] [<link>]\n" }, { "set-linkprop", do_set_linkprop, - " set-linkprop [-t] -p <prop>=<value>[,...] <name>" }, + " set-linkprop [-t] [-z zonename] -p <prop>=<value>[,...] " + "<name>" }, { "reset-linkprop", do_reset_linkprop, - " reset-linkprop [-t] [-p <prop>,...] <name>" }, + " reset-linkprop [-t] [-z zonename] [-p <prop>,...] <name>"}, { "show-linkprop", do_show_linkprop, - " show-linkprop [-cP] [-o <field>,...] [-p <prop>,...] " - "<name>\n" }, + " show-linkprop [-cP] [-o <field>,...] [-z zonename] " + "[-p <prop>,...] <name>\n" }, { "show-ether", do_show_ether, " show-ether [-px][-o <field>,...] <link>\n" }, { "create-secobj", do_create_secobj, @@ -347,10 +358,10 @@ static cmd_t cmds[] = { "\t\t {vrrp -V <vrid> -A {inet | inet6}} [-v <vid> [-f]]\n" "\t\t [-p <prop>=<value>[,...]] <vnic-link>" }, { "delete-vnic", do_delete_vnic, - " delete-vnic [-t] <vnic-link>" }, + " delete-vnic [-t] [-z zonename] <vnic-link>" }, { "show-vnic", do_show_vnic, - " show-vnic [-pP] [-l <link>] [-s [-i <interval>]] " - "[<link>]\n" }, + " show-vnic [-pP] [-l <link>] [-z zonename] " + "[-s [-i <interval>]] [<link>]\n" }, { "up-vnic", do_up_vnic, NULL }, { "create-part", do_create_part, " create-part [-t] [-f] -l <link> [-P <pkey>]\n" @@ -401,6 +412,17 @@ static cmd_t cmds[] = { " <bridge>\n" " show-bridge -t [-p] [-o <field>,...] [-s [-i <interval>]]" " <bridge>\n" }, + { "create-overlay", do_create_overlay, + " create-overlay [-t] -e <encap> -s <search> -v <vnetid>\n" + "\t\t [ -p <prop>=<value>[,...]] <overlay>" }, + { "delete-overlay", do_delete_overlay, + " delete-overlay <overlay>" }, + { "modify-overlay", do_modify_overlay, + " modify-overlay -d mac | -f | -s mac=ip:port " + "<overlay>" }, + { "show-overlay", do_show_overlay, + " show-overlay [-f | -t] [[-p] -o <field>,...] " + "[<overlay>]\n" }, { "show-usage", do_show_usage, " show-usage [-a] [-d | -F <format>] " "[-s <DD/MM/YYYY,HH:MM:SS>]\n" @@ -959,6 +981,7 @@ typedef struct show_linkprop_state { char ls_link[MAXLINKNAMELEN]; char *ls_line; char **ls_propvals; + char *ls_zonename; dladm_arg_list_t *ls_proplist; boolean_t ls_parsable; boolean_t ls_persist; @@ -1011,21 +1034,24 @@ typedef struct vnic_fields_buf_s char vnic_macaddr[18]; char vnic_macaddrtype[19]; char vnic_vid[6]; + char vnic_zone[ZONENAME_MAX]; } vnic_fields_buf_t; static const ofmt_field_t vnic_fields[] = { { "LINK", 13, offsetof(vnic_fields_buf_t, vnic_link), print_default_cb}, -{ "OVER", 13, +{ "OVER", 11, offsetof(vnic_fields_buf_t, vnic_over), print_default_cb}, -{ "SPEED", 7, +{ "SPEED", 6, offsetof(vnic_fields_buf_t, vnic_speed), print_default_cb}, { "MACADDRESS", 18, offsetof(vnic_fields_buf_t, vnic_macaddr), print_default_cb}, -{ "MACADDRTYPE", 20, +{ "MACADDRTYPE", 12, offsetof(vnic_fields_buf_t, vnic_macaddrtype), print_default_cb}, -{ "VID", 7, +{ "VID", 5, offsetof(vnic_fields_buf_t, vnic_vid), print_default_cb}, +{ "ZONE", 20, + offsetof(vnic_fields_buf_t, vnic_zone), print_default_cb}, { NULL, 0, 0, NULL}} ; @@ -1425,6 +1451,82 @@ static ofmt_field_t bridge_trill_fields[] = { offsetof(bridge_trill_fields_buf_t, bridget_nexthop), print_default_cb }, { NULL, 0, 0, NULL}}; +static const struct option overlay_create_lopts[] = { + { "encap", required_argument, NULL, 'e' }, + { "prop", required_argument, NULL, 'p' }, + { "search", required_argument, NULL, 's' }, + { "temporary", no_argument, NULL, 't' }, + { "vnetid", required_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +static const struct option overlay_modify_lopts[] = { + { "delete-entry", required_argument, NULL, 'd' }, + { "flush-table", no_argument, NULL, 'f' }, + { "set-entry", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } +}; + +static const struct option overlay_show_lopts[] = { + { "fma", no_argument, NULL, 'f' }, + { "target", no_argument, NULL, 't' }, + { "parsable", no_argument, NULL, 'p' }, + { "parseable", no_argument, NULL, 'p' }, + { "output", required_argument, NULL, 'o' }, + { NULL, 0, NULL, 0 } +}; + +/* + * Structures for dladm show-overlay + */ +typedef enum { + OVERLAY_LINK, + OVERLAY_PROPERTY, + OVERLAY_PERM, + OVERLAY_REQ, + OVERLAY_VALUE, + OVERLAY_DEFAULT, + OVERLAY_POSSIBLE +} overlay_field_index_t; + +static const ofmt_field_t overlay_fields[] = { +/* name, field width, index */ +{ "LINK", 19, OVERLAY_LINK, print_overlay_cb }, +{ "PROPERTY", 19, OVERLAY_PROPERTY, print_overlay_cb }, +{ "PERM", 5, OVERLAY_PERM, print_overlay_cb }, +{ "REQ", 4, OVERLAY_REQ, print_overlay_cb }, +{ "VALUE", 11, OVERLAY_VALUE, print_overlay_cb }, +{ "DEFAULT", 10, OVERLAY_DEFAULT, print_overlay_cb }, +{ "POSSIBLE", 10, OVERLAY_POSSIBLE, print_overlay_cb }, +{ NULL, 0, 0, NULL } +}; + +typedef enum { + OVERLAY_FMA_LINK, + OVERLAY_FMA_STATUS, + OVERLAY_FMA_DETAILS +} overlay_fma_field_index_t; + +static const ofmt_field_t overlay_fma_fields[] = { +{ "LINK", 20, OVERLAY_FMA_LINK, print_overlay_fma_cb }, +{ "STATUS", 8, OVERLAY_FMA_STATUS, print_overlay_fma_cb }, +{ "DETAILS", 52, OVERLAY_FMA_DETAILS, print_overlay_fma_cb }, +{ NULL, 0, 0, NULL } +}; + +typedef enum { + OVERLAY_TARG_LINK, + OVERLAY_TARG_TARGET, + OVERLAY_TARG_DEST +} overlay_targ_field_index_t; + +static const ofmt_field_t overlay_targ_fields[] = { +{ "LINK", 20, OVERLAY_TARG_LINK, print_overlay_targ_cb }, +{ "TARGET", 18, OVERLAY_TARG_TARGET, print_overlay_targ_cb }, +{ "DESTINATION", 42, OVERLAY_TARG_DEST, print_overlay_targ_cb }, +{ NULL, 0, 0, NULL } +}; + static char *progname; static sig_atomic_t signalled; @@ -1434,6 +1536,12 @@ static sig_atomic_t signalled; */ static dladm_handle_t handle = NULL; +/* + * Global error list that all routines can use. It's initialized by the main + * code. + */ +static dladm_errlist_t errlist; + #define DLADM_ETHERSTUB_NAME "etherstub" #define DLADM_IS_ETHERSTUB(id) (id == DATALINK_INVALID_LINKID) @@ -1484,6 +1592,8 @@ main(int argc, char *argv[]) "could not open /dev/dld"); } + dladm_errlist_init(&errlist); + cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage); dladm_close(handle); @@ -2495,13 +2605,17 @@ do_rename_link(int argc, char *argv[], const char *use) char *link1, *link2; char *altroot = NULL; dladm_status_t status; + char *zonename = NULL; opterr = 0; - while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) { + while ((option = getopt_long(argc, argv, ":R:z:", lopts, NULL)) != -1) { switch (option) { case 'R': altroot = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); break; @@ -2517,7 +2631,7 @@ do_rename_link(int argc, char *argv[], const char *use) link1 = argv[optind++]; link2 = argv[optind]; - if ((status = dladm_rename_link(handle, link1, link2)) != + if ((status = dladm_rename_link(handle, zonename, link1, link2)) != DLADM_STATUS_OK) die_dlerr(status, "rename operation failed"); } @@ -3407,11 +3521,12 @@ do_show_link(int argc, char *argv[], const char *use) ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPsSi:o:", + while ((option = getopt_long(argc, argv, ":pPsSi:o:z:", show_lopts, NULL)) != -1) { switch (option) { case 'p': @@ -3450,6 +3565,9 @@ do_show_link(int argc, char *argv[], const char *use) if (!dladm_str2interval(optarg, &interval)) die("invalid interval value '%s'", optarg); break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); break; @@ -3475,8 +3593,8 @@ do_show_link(int argc, char *argv[], const char *use) if (strlcpy(linkname, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long"); - if ((status = dladm_name2info(handle, linkname, &linkid, &f, - NULL, NULL)) != DLADM_STATUS_OK) { + if ((status = dladm_zname2info(handle, zonename, linkname, + &linkid, &f, NULL, NULL)) != DLADM_STATUS_OK) { die_dlerr(status, "link %s is not valid", linkname); } @@ -4741,6 +4859,12 @@ do_create_vnic(int argc, char *argv[], const char *use) if ((flags & DLADM_OPT_FORCE) != 0 && vid == 0) die("-f option can only be used with -v"); + /* + * If creating a transient VNIC for a zone, mark it in the kernel. + */ + if (strstr(propstr, "zone=") != NULL && !(flags & DLADM_OPT_PERSIST)) + flags |= DLADM_OPT_TRANSIENT; + if (mac_prefix_len != 0 && mac_addr_type != VNIC_MAC_ADDR_TYPE_RANDOM && mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED) usage(); @@ -4785,7 +4909,7 @@ do_create_vnic(int argc, char *argv[], const char *use) status = dladm_vnic_create(handle, name, dev_linkid, mac_addr_type, mac_addr, maclen, &mac_slot, mac_prefix_len, vid, vrid, af, - &linkid, proplist, flags); + &linkid, proplist, &errlist, flags); switch (status) { case DLADM_STATUS_OK: break; @@ -4796,7 +4920,8 @@ do_create_vnic(int argc, char *argv[], const char *use) break; default: - die_dlerr(status, "vnic creation over %s failed", devname); + die_dlerrlist(status, &errlist, "vnic creation over %s failed", + devname); } dladm_free_props(proplist); @@ -4832,9 +4957,10 @@ do_delete_vnic_common(int argc, char *argv[], const char *use, datalink_id_t linkid; char *altroot = NULL; dladm_status_t status; + char *zonename = NULL; opterr = 0; - while ((option = getopt_long(argc, argv, ":R:t", lopts, + while ((option = getopt_long(argc, argv, ":R:tz:", lopts, NULL)) != -1) { switch (option) { case 't': @@ -4843,6 +4969,9 @@ do_delete_vnic_common(int argc, char *argv[], const char *use, case 'R': altroot = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); } @@ -4855,8 +4984,8 @@ do_delete_vnic_common(int argc, char *argv[], const char *use, if (altroot != NULL) altroot_cmd(altroot, argc, argv); - status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL, - NULL); + status = dladm_zname2info(handle, zonename, argv[optind], &linkid, NULL, + NULL, NULL); if (status != DLADM_STATUS_OK) die("invalid link name '%s'", argv[optind]); @@ -4988,6 +5117,9 @@ print_vnic(show_vnic_state_t *state, datalink_id_t linkid) char vnic_name[MAXLINKNAMELEN]; char mstr[MAXMACADDRLEN * 3]; vnic_fields_buf_t vbuf; + uint_t valcnt = 1; + char zonename[DLADM_PROP_VAL_MAX + 1]; + char *valptr[1]; if ((status = dladm_vnic_info(handle, linkid, vnic, state->vs_flags)) != DLADM_STATUS_OK) @@ -5017,6 +5149,18 @@ print_vnic(show_vnic_state_t *state, datalink_id_t linkid) NULL, devname, sizeof (devname)) != DLADM_STATUS_OK) (void) sprintf(devname, "?"); + + zonename[0] = '\0'; + if (!is_etherstub) { + valptr[0] = zonename; + (void) dladm_get_linkprop(handle, linkid, + DLADM_PROP_VAL_CURRENT, "zone", (char **)valptr, &valcnt); + } + + if (state->vs_zonename != NULL && + strcmp(state->vs_zonename, zonename) != 0) + return (DLADM_STATUS_OK); + state->vs_found = B_TRUE; if (state->vs_stats) { /* print vnic statistics */ @@ -5092,6 +5236,13 @@ print_vnic(show_vnic_state_t *state, datalink_id_t linkid) (void) snprintf(vbuf.vnic_vid, sizeof (vbuf.vnic_vid), "%d", vnic->va_vid); + + if (zonename[0] != '\0') + (void) snprintf(vbuf.vnic_zone, + sizeof (vbuf.vnic_zone), "%s", zonename); + else + (void) strlcpy(vbuf.vnic_zone, "--", + sizeof (vbuf.vnic_zone)); } ofmt_print(state->vs_ofmt, &vbuf); @@ -5130,10 +5281,11 @@ do_show_vnic_common(int argc, char *argv[], const char *use, ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPl:si:o:", lopts, + while ((option = getopt_long(argc, argv, ":pPl:si:o:z:", lopts, NULL)) != -1) { switch (option) { case 'p': @@ -5172,6 +5324,9 @@ do_show_vnic_common(int argc, char *argv[], const char *use, o_arg = B_TRUE; fields_str = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); } @@ -5182,8 +5337,8 @@ do_show_vnic_common(int argc, char *argv[], const char *use, /* get vnic ID (optional last argument) */ if (optind == (argc - 1)) { - status = dladm_name2info(handle, argv[optind], &linkid, NULL, - NULL, NULL); + status = dladm_zname2info(handle, zonename, argv[optind], + &linkid, NULL, NULL, NULL); if (status != DLADM_STATUS_OK) { die_dlerr(status, "invalid vnic name '%s'", argv[optind]); @@ -5194,8 +5349,8 @@ do_show_vnic_common(int argc, char *argv[], const char *use, } if (l_arg) { - status = dladm_name2info(handle, state.vs_link, &dev_linkid, - NULL, NULL, NULL); + status = dladm_zname2info(handle, zonename, state.vs_link, + &dev_linkid, NULL, NULL, NULL); if (status != DLADM_STATUS_OK) { die_dlerr(status, "invalid link name '%s'", state.vs_link); @@ -5207,6 +5362,7 @@ do_show_vnic_common(int argc, char *argv[], const char *use, state.vs_etherstub = etherstub; state.vs_found = B_FALSE; state.vs_flags = flags; + state.vs_zonename = zonename; if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { if (etherstub) @@ -5295,7 +5451,7 @@ do_create_etherstub(int argc, char *argv[], const char *use) status = dladm_vnic_create(handle, name, DATALINK_INVALID_LINKID, VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0, - VRRP_VRID_NONE, AF_UNSPEC, NULL, NULL, flags); + VRRP_VRID_NONE, AF_UNSPEC, NULL, NULL, &errlist, flags); if (status != DLADM_STATUS_OK) die_dlerr(status, "etherstub creation failed"); } @@ -6695,6 +6851,7 @@ do_show_linkprop(int argc, char **argv, const char *use) ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(propstr, DLADM_STRSIZE); opterr = 0; @@ -6705,7 +6862,7 @@ do_show_linkprop(int argc, char **argv, const char *use) state.ls_header = B_TRUE; state.ls_retstatus = DLADM_STATUS_OK; - while ((option = getopt_long(argc, argv, ":p:cPo:", + while ((option = getopt_long(argc, argv, ":p:cPo:z:", prop_longopts, NULL)) != -1) { switch (option) { case 'p': @@ -6724,6 +6881,9 @@ do_show_linkprop(int argc, char **argv, const char *use) case 'o': fields_str = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); break; @@ -6731,8 +6891,8 @@ do_show_linkprop(int argc, char **argv, const char *use) } if (optind == (argc - 1)) { - if ((status = dladm_name2info(handle, argv[optind], &linkid, - NULL, NULL, NULL)) != DLADM_STATUS_OK) { + if ((status = dladm_zname2info(handle, zonename, argv[optind], + &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) { die_dlerr(status, "link %s is not valid", argv[optind]); } } else if (optind != argc) { @@ -6743,6 +6903,7 @@ do_show_linkprop(int argc, char **argv, const char *use) != DLADM_STATUS_OK) die("invalid link properties specified"); state.ls_proplist = proplist; + state.ls_zonename = zonename; state.ls_status = DLADM_STATUS_OK; if (state.ls_parsable) @@ -6787,6 +6948,17 @@ show_linkprop_onelink(dladm_handle_t hdl, datalink_id_t linkid, void *arg) return (DLADM_WALK_CONTINUE); } + if (statep->ls_zonename != NULL) { + datalink_id_t tlinkid; + + if (dladm_zname2info(hdl, statep->ls_zonename, statep->ls_link, + &tlinkid, NULL, NULL, NULL) != DLADM_STATUS_OK || + linkid != tlinkid) { + statep->ls_status = DLADM_STATUS_NOTFOUND; + return (DLADM_WALK_CONTINUE); + } + } + if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) || (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) { statep->ls_status = DLADM_STATUS_BADARG; @@ -6869,11 +7041,12 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) dladm_status_t status = DLADM_STATUS_OK; char propstr[DLADM_STRSIZE]; dladm_arg_list_t *proplist = NULL; + char *zonename = NULL; opterr = 0; bzero(propstr, DLADM_STRSIZE); - while ((option = getopt_long(argc, argv, ":p:R:t", + while ((option = getopt_long(argc, argv, ":p:R:tz:", prop_longopts, NULL)) != -1) { switch (option) { case 'p': @@ -6888,6 +7061,9 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) case 'R': altroot = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); @@ -6910,8 +7086,8 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) altroot_cmd(altroot, argc, argv); } - status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL, - NULL); + status = dladm_zname2info(handle, zonename, argv[optind], &linkid, + NULL, NULL, NULL); if (status != DLADM_STATUS_OK) die_dlerr(status, "link %s is not valid", argv[optind]); @@ -8934,6 +9110,21 @@ warn_dlerr(dladm_status_t err, const char *format, ...) (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); } +static void +warn_dlerrlist(dladm_errlist_t *errlist) +{ + if (errlist != NULL && errlist->el_count > 0) { + int i; + for (i = 0; i < errlist->el_count; i++) { + (void) fprintf(stderr, gettext("%s: warning: "), + progname); + + (void) fprintf(stderr, "%s\n", + gettext(errlist->el_errs[i])); + } + } +} + /* * Also closes the dladm handle if it is not NULL. */ @@ -8959,6 +9150,34 @@ die_dlerr(dladm_status_t err, const char *format, ...) exit(EXIT_FAILURE); } +/* + * Like die_dlerr, but uses the errlist for additional information. + */ +/* PRINTFLIKE3 */ +static void +die_dlerrlist(dladm_status_t err, dladm_errlist_t *errlist, + const char *format, ...) +{ + va_list alist; + char errmsg[DLADM_STRSIZE]; + + warn_dlerrlist(errlist); + format = gettext(format); + (void) fprintf(stderr, "%s: ", progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); + + /* close dladm handle if it was opened */ + if (handle != NULL) + dladm_close(handle); + + exit(EXIT_FAILURE); + +} + /* PRINTFLIKE1 */ static void die(const char *format, ...) @@ -9689,3 +9908,673 @@ do_up_part(int argc, char *argv[], const char *use) (void) dladm_part_up(handle, partid, 0); } + +static void +do_create_overlay(int argc, char *argv[], const char *use) +{ + int opt; + char *encap = NULL, *endp, *search = NULL; + char name[MAXLINKNAMELEN]; + dladm_status_t status; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + uint64_t vid; + boolean_t havevid = B_FALSE; + char propstr[DLADM_STRSIZE]; + dladm_arg_list_t *proplist = NULL; + + bzero(propstr, sizeof (propstr)); + while ((opt = getopt_long(argc, argv, ":te:v:p:s:", + overlay_create_lopts, NULL)) != -1) { + switch (opt) { + case 'e': + encap = optarg; + break; + case 's': + search = optarg; + break; + case 't': + flags &= ~DLADM_OPT_PERSIST; + break; + case 'p': + (void) strlcat(propstr, optarg, DLADM_STRSIZE); + if (strlcat(propstr, ",", DLADM_STRSIZE) >= + DLADM_STRSIZE) + die("property list too long '%s'", propstr); + break; + case 'v': + vid = strtoul(optarg, &endp, 10); + if (*endp != '\0' || (vid == 0 && errno == EINVAL)) + die("couldn't parse virtual networkd id: %s", + optarg); + if (vid == ULONG_MAX && errno == ERANGE) + die("virtual networkd id too large: %s", + optarg); + havevid = B_TRUE; + break; + default: + die_opterr(optopt, opt, use); + } + } + + if (havevid == B_FALSE) + die("missing required virtual network id"); + + if (encap == NULL) + die("missing required encapsulation plugin"); + + if (encap == NULL) + die("missing required search plugin"); + + if (optind != (argc - 1)) + die("missing device name"); + + if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN) + die("link name too long '%s'", argv[optind]); + + if (!dladm_valid_linkname(name)) + die("invalid link name '%s'", argv[optind]); + + if (strlen(encap) + 1 > MAXLINKNAMELEN) + die("encapsulation plugin name too long '%s'", encap); + + if (strlen(search) + 1 > MAXLINKNAMELEN) + die("search plugin name too long '%s'", encap); + + if (dladm_parse_link_props(propstr, &proplist, B_FALSE) + != DLADM_STATUS_OK) + die("invalid overlay property"); + + status = dladm_overlay_create(handle, name, encap, search, vid, + proplist, &errlist, flags); + dladm_free_props(proplist); + if (status != DLADM_STATUS_OK) { + die_dlerrlist(status, &errlist, "overlay creation failed"); + } +} + +/* ARGSUSED */ +static void +do_delete_overlay(int argc, char *argv[], const char *use) +{ + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + + if (argc != 2) { + usage(); + } + + status = dladm_name2info(handle, argv[1], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to delete %s", argv[1]); + + status = dladm_overlay_delete(handle, linkid); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to delete %s", argv[1]); +} + +typedef struct showoverlay_state { + ofmt_handle_t sho_ofmt; + const char *sho_linkname; + dladm_overlay_propinfo_handle_t sho_info; + uint8_t sho_value[DLADM_OVERLAY_PROP_SIZEMAX]; + uint32_t sho_size; +} showoverlay_state_t; + +typedef struct showoverlay_fma_state { + ofmt_handle_t shof_ofmt; + const char *shof_linkname; + dladm_overlay_status_t *shof_status; +} showoverlay_fma_state_t; + +typedef struct showoverlay_targ_state { + ofmt_handle_t shot_ofmt; + const char *shot_linkname; + const struct ether_addr *shot_key; + const dladm_overlay_point_t *shot_point; +} showoverlay_targ_state_t; + +static void +print_overlay_value(char *outbuf, uint_t bufsize, uint_t type, const void *pbuf, + const size_t psize) +{ + const struct in6_addr *ipv6; + struct in_addr ip; + + switch (type) { + case OVERLAY_PROP_T_INT: + if (psize != 1 && psize != 2 && psize != 4 && psize != 8) { + (void) snprintf(outbuf, bufsize, "?"); + break; + } + if (psize == 1) + (void) snprintf(outbuf, bufsize, "%d", *(int8_t *)pbuf); + if (psize == 2) + (void) snprintf(outbuf, bufsize, "%d", + *(int16_t *)pbuf); + if (psize == 4) + (void) snprintf(outbuf, bufsize, "%d", + *(int32_t *)pbuf); + if (psize == 8) + (void) snprintf(outbuf, bufsize, "%d", + *(int64_t *)pbuf); + break; + case OVERLAY_PROP_T_UINT: + if (psize != 1 && psize != 2 && psize != 4 && psize != 8) { + (void) snprintf(outbuf, bufsize, "?"); + break; + } + if (psize == 1) + (void) snprintf(outbuf, bufsize, "%d", + *(uint8_t *)pbuf); + if (psize == 2) + (void) snprintf(outbuf, bufsize, "%d", + *(uint16_t *)pbuf); + if (psize == 4) + (void) snprintf(outbuf, bufsize, "%d", + *(uint32_t *)pbuf); + if (psize == 8) + (void) snprintf(outbuf, bufsize, "%d", + *(uint64_t *)pbuf); + break; + case OVERLAY_PROP_T_IP: + if (psize != sizeof (struct in6_addr)) { + warn("malformed overlay IP property: %d bytes\n", + psize); + (void) snprintf(outbuf, bufsize, "--"); + break; + } + + ipv6 = pbuf; + if (IN6_IS_ADDR_V4MAPPED(ipv6)) { + IN6_V4MAPPED_TO_INADDR(ipv6, &ip); + if (inet_ntop(AF_INET, &ip, outbuf, bufsize) == NULL) { + warn("malformed overlay IP property\n"); + (void) snprintf(outbuf, bufsize, "--"); + break; + } + } else { + if (inet_ntop(AF_INET6, ipv6, outbuf, bufsize) == + NULL) { + warn("malformed overlay IP property\n"); + (void) snprintf(outbuf, bufsize, "--"); + break; + } + } + + break; + case OVERLAY_PROP_T_STRING: + (void) snprintf(outbuf, bufsize, "%s", pbuf); + break; + default: + abort(); + } + + return; + +} + +static boolean_t +print_overlay_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + dladm_status_t status; + showoverlay_state_t *sp = ofarg->ofmt_cbarg; + dladm_overlay_propinfo_handle_t infop = sp->sho_info; + const char *pname; + uint_t type, prot; + const void *def; + uint32_t defsize; + const mac_propval_range_t *rangep; + + if ((status = dladm_overlay_prop_info(infop, &pname, &type, &prot, &def, + &defsize, &rangep)) != DLADM_STATUS_OK) { + warn_dlerr(status, "failed to get get property info"); + return (B_TRUE); + } + + switch (ofarg->ofmt_id) { + case OVERLAY_LINK: + (void) snprintf(buf, bufsize, "%s", sp->sho_linkname); + break; + case OVERLAY_PROPERTY: + (void) snprintf(buf, bufsize, "%s", pname); + break; + case OVERLAY_PERM: + if ((prot & OVERLAY_PROP_PERM_RW) == OVERLAY_PROP_PERM_RW) { + (void) snprintf(buf, bufsize, "%s", "rw"); + } else if ((prot & OVERLAY_PROP_PERM_RW) == + OVERLAY_PROP_PERM_READ) { + (void) snprintf(buf, bufsize, "%s", "r-"); + } else { + (void) snprintf(buf, bufsize, "%s", "--"); + } + break; + case OVERLAY_REQ: + (void) snprintf(buf, bufsize, "%s", + prot & OVERLAY_PROP_PERM_REQ ? "y" : "-"); + break; + case OVERLAY_VALUE: + if (sp->sho_size == 0) { + (void) snprintf(buf, bufsize, "%s", "--"); + } else { + print_overlay_value(buf, bufsize, type, sp->sho_value, + sp->sho_size); + } + break; + case OVERLAY_DEFAULT: + if (defsize == 0) { + (void) snprintf(buf, bufsize, "%s", "--"); + } else { + print_overlay_value(buf, bufsize, type, def, defsize); + } + break; + case OVERLAY_POSSIBLE: { + int i; + char **vals, *ptr, *lim; + if (rangep->mpr_count == 0) { + (void) snprintf(buf, bufsize, "%s", "--"); + break; + } + + vals = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * + rangep->mpr_count); + if (vals == NULL) + die("insufficient memory"); + for (i = 0; i < rangep->mpr_count; i++) { + vals[i] = (char *)vals + sizeof (char *) * + rangep->mpr_count + i * DLADM_MAX_PROP_VALCNT; + } + + if (dladm_range2strs(rangep, vals) != 0) { + free(vals); + (void) snprintf(buf, bufsize, "%s", "?"); + break; + } + + ptr = buf; + lim = buf + bufsize; + for (i = 0; i < rangep->mpr_count; i++) { + ptr += snprintf(ptr, lim - ptr, "%s,", vals[i]); + if (ptr >= lim) + break; + } + if (rangep->mpr_count > 0) + buf[strlen(buf) - 1] = '\0'; + free(vals); + break; + } + default: + abort(); + } + return (B_TRUE); +} + +static int +dladm_overlay_show_one(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_propinfo_handle_t phdl, void *arg) +{ + showoverlay_state_t *sp = arg; + sp->sho_info = phdl; + + sp->sho_size = sizeof (sp->sho_value); + if (dladm_overlay_get_prop(handle, linkid, phdl, &sp->sho_value, + &sp->sho_size) != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + ofmt_print(sp->sho_ofmt, sp); + return (DLADM_WALK_CONTINUE); +} + +static int +show_one_overlay(dladm_handle_t hdl, datalink_id_t linkid, void *arg) +{ + char buf[MAXLINKNAMELEN]; + showoverlay_state_t state; + datalink_class_t class; + + if (dladm_datalink_id2info(hdl, linkid, NULL, &class, NULL, buf, + MAXLINKNAMELEN) != DLADM_STATUS_OK || + class != DATALINK_CLASS_OVERLAY) + return (DLADM_WALK_CONTINUE); + + state.sho_linkname = buf; + state.sho_ofmt = arg; + + dladm_errlist_reset(&errlist); + (void) dladm_overlay_walk_prop(handle, linkid, dladm_overlay_show_one, + &state, &errlist); + warn_dlerrlist(&errlist); + + return (DLADM_WALK_CONTINUE); +} + +static boolean_t +print_overlay_targ_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + char keybuf[ETHERADDRSTRL]; + const showoverlay_targ_state_t *shot = ofarg->ofmt_cbarg; + const dladm_overlay_point_t *point = shot->shot_point; + char macbuf[ETHERADDRSTRL]; + char ipbuf[INET6_ADDRSTRLEN]; + custr_t *cus; + + switch (ofarg->ofmt_id) { + case OVERLAY_TARG_LINK: + (void) snprintf(buf, bufsize, shot->shot_linkname); + break; + case OVERLAY_TARG_TARGET: + if ((point->dop_flags & DLADM_OVERLAY_F_DEFAULT) != 0) { + (void) snprintf(buf, bufsize, "*:*:*:*:*:*"); + } else { + if (ether_ntoa_r(shot->shot_key, keybuf) == NULL) { + warn("encountered malformed mac address key\n"); + return (B_FALSE); + } + (void) snprintf(buf, bufsize, "%s", keybuf); + } + break; + case OVERLAY_TARG_DEST: + if (custr_alloc_buf(&cus, buf, bufsize) != 0) { + die("ran out of memory for printing the overlay " + "target destination"); + } + + if (point->dop_dest & OVERLAY_PLUGIN_D_ETHERNET) { + if (ether_ntoa_r(&point->dop_mac, macbuf) == NULL) { + warn("encountered malformed mac address target " + "for key %s\n", keybuf); + return (B_FALSE); + } + (void) custr_append(cus, macbuf); + } + + if (point->dop_dest & OVERLAY_PLUGIN_D_IP) { + if (IN6_IS_ADDR_V4MAPPED(&point->dop_ip)) { + struct in_addr v4; + IN6_V4MAPPED_TO_INADDR(&point->dop_ip, &v4); + if (inet_ntop(AF_INET, &v4, ipbuf, + sizeof (ipbuf)) == NULL) + abort(); + } else if (inet_ntop(AF_INET6, &point->dop_ip, ipbuf, + sizeof (ipbuf)) == NULL) { + /* + * The only failrues we should get are + * EAFNOSUPPORT and ENOSPC because of buffer + * exhaustion. In either of these cases, that + * means something has gone horribly wrong. + */ + abort(); + } + if (point->dop_dest & OVERLAY_PLUGIN_D_ETHERNET) + (void) custr_appendc(cus, ','); + (void) custr_append(cus, ipbuf); + } + + if (point->dop_dest & OVERLAY_PLUGIN_D_PORT) { + if (point->dop_dest & OVERLAY_PLUGIN_D_IP) + (void) custr_appendc(cus, ':'); + else if (point->dop_dest & OVERLAY_PLUGIN_D_ETHERNET) + (void) custr_appendc(cus, ','); + (void) custr_append_printf(cus, "%u", point->dop_port); + } + + custr_free(cus); + + break; + } + return (B_TRUE); +} + +/* ARGSUSED */ +static int +show_one_overlay_table_entry(dladm_handle_t handle, datalink_id_t linkid, + const struct ether_addr *key, const dladm_overlay_point_t *point, void *arg) +{ + showoverlay_targ_state_t *shot = arg; + + shot->shot_key = key; + shot->shot_point = point; + ofmt_print(shot->shot_ofmt, shot); + + return (DLADM_WALK_CONTINUE); +} + +/* ARGSUSED */ +static int +show_one_overlay_table(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + char linkbuf[MAXLINKNAMELEN]; + showoverlay_targ_state_t shot; + datalink_class_t class; + + if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, linkbuf, + MAXLINKNAMELEN) != DLADM_STATUS_OK || + class != DATALINK_CLASS_OVERLAY) + return (DLADM_WALK_CONTINUE); + + shot.shot_ofmt = arg; + shot.shot_linkname = linkbuf; + + (void) dladm_overlay_walk_cache(handle, linkid, + show_one_overlay_table_entry, &shot); + + return (DLADM_WALK_CONTINUE); +} + +static boolean_t +print_overlay_fma_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + showoverlay_fma_state_t *shof = ofarg->ofmt_cbarg; + dladm_overlay_status_t *st = shof->shof_status; + + switch (ofarg->ofmt_id) { + case OVERLAY_FMA_LINK: + (void) snprintf(buf, bufsize, "%s", shof->shof_linkname); + break; + case OVERLAY_FMA_STATUS: + (void) snprintf(buf, bufsize, st->dos_degraded == B_TRUE ? + "DEGRADED": "ONLINE"); + break; + case OVERLAY_FMA_DETAILS: + (void) snprintf(buf, bufsize, "%s", st->dos_degraded == B_TRUE ? + st->dos_fmamsg : "-"); + break; + default: + abort(); + } + return (B_TRUE); +} + +/* ARGSUSED */ +static void +show_one_overlay_fma_cb(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_status_t *stat, void *arg) +{ + showoverlay_fma_state_t *shof = arg; + shof->shof_status = stat; + ofmt_print(shof->shof_ofmt, shof); +} + + +static int +show_one_overlay_fma(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + dladm_status_t status; + char linkbuf[MAXLINKNAMELEN]; + datalink_class_t class; + showoverlay_fma_state_t shof; + + if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, linkbuf, + MAXLINKNAMELEN) != DLADM_STATUS_OK || + class != DATALINK_CLASS_OVERLAY) { + die("datalink %s is not an overlay device\n", linkbuf); + } + + shof.shof_ofmt = arg; + shof.shof_linkname = linkbuf; + + status = dladm_overlay_status(handle, linkid, + show_one_overlay_fma_cb, &shof); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to obtain device status for %s", + linkbuf); + + return (DLADM_WALK_CONTINUE); +} + +static void +do_show_overlay(int argc, char *argv[], const char *use) +{ + int i, opt; + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + int (*funcp)(dladm_handle_t, datalink_id_t, void *); + char *fields_str = NULL; + const ofmt_field_t *fieldsp; + ofmt_status_t oferr; + boolean_t parse; + ofmt_handle_t ofmt; + uint_t ofmtflags; + int err; + + + funcp = show_one_overlay; + fieldsp = overlay_fields; + parse = B_FALSE; + ofmtflags = OFMT_WRAP; + while ((opt = getopt_long(argc, argv, ":o:pft", overlay_show_lopts, + NULL)) != -1) { + switch (opt) { + case 'f': + funcp = show_one_overlay_fma; + fieldsp = overlay_fma_fields; + break; + case 'o': + fields_str = optarg; + break; + case 'p': + parse = B_TRUE; + ofmtflags = OFMT_PARSABLE; + break; + case 't': + funcp = show_one_overlay_table; + fieldsp = overlay_targ_fields; + break; + default: + die_opterr(optopt, opt, use); + } + } + + if (fields_str != NULL && strcasecmp(fields_str, "all") == 0) + fields_str = NULL; + + oferr = ofmt_open(fields_str, fieldsp, ofmtflags, 0, &ofmt); + dladm_ofmt_check(oferr, parse, ofmt); + + err = 0; + if (argc > optind) { + for (i = optind; i < argc; i++) { + status = dladm_name2info(handle, argv[i], &linkid, + NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) { + warn_dlerr(status, "failed to find %s", + argv[i]); + err = 1; + continue; + } + funcp(handle, linkid, ofmt); + } + } else { + (void) dladm_walk_datalink_id(funcp, handle, ofmt, + DATALINK_CLASS_OVERLAY, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + } + ofmt_close(ofmt); + + exit(err); +} + +static void +do_modify_overlay(int argc, char *argv[], const char *use) +{ + int opt, ocnt = 0; + boolean_t flush, set, delete; + struct ether_addr e; + char *dest; + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + + flush = set = delete = B_FALSE; + while ((opt = getopt_long(argc, argv, ":fd:s:", overlay_modify_lopts, + NULL)) != -1) { + switch (opt) { + case 'd': + if (delete == B_TRUE) + die_optdup('d'); + delete = B_TRUE; + ocnt++; + if (ether_aton_r(optarg, &e) == NULL) + die("invalid mac address: %s\n", optarg); + break; + case 'f': + if (flush == B_TRUE) + die_optdup('f'); + flush = B_TRUE; + ocnt++; + break; + case 's': + if (set == B_TRUE) + die_optdup('s'); + set = B_TRUE; + ocnt++; + dest = strchr(optarg, '='); + *dest = '\0'; + dest++; + if (dest == NULL) + die("malformed value, expected mac=dest, " + "got: %s\n", optarg); + if (ether_aton_r(optarg, &e) == NULL) + die("invalid mac address: %s\n", optarg); + break; + default: + die_opterr(optopt, opt, use); + } + } + + if (ocnt == 0) + die("need to specify one of -d, -f, or -s"); + if (ocnt > 1) + die("only one of -d, -f, or -s may be used"); + + if (argv[optind] == NULL) + die("missing required overlay device\n"); + if (argc > optind + 1) + die("only one overlay device may be specified\n"); + + status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL, + NULL); + if (status != DLADM_STATUS_OK) { + die_dlerr(status, "failed to find overlay %s", argv[optind]); + } + + if (flush == B_TRUE) { + status = dladm_overlay_cache_flush(handle, linkid); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to flush target cache for " + "overlay %s", argv[optind]); + } + + if (delete == B_TRUE) { + status = dladm_overlay_cache_delete(handle, linkid, &e); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to flush target %s from " + "overlay target cache %s", optarg, argv[optind]); + } + + if (set == B_TRUE) { + status = dladm_overlay_cache_set(handle, linkid, &e, dest); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to set target %s for overlay " + "target cache %s", optarg, argv[optind]); + } + +} diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_db.c b/usr/src/cmd/dlmgmtd/dlmgmt_db.c index 99307dbc03..8eecc807e5 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_db.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_db.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015, Joyent Inc. */ #include <assert.h> @@ -43,6 +44,8 @@ #include <libcontract.h> #include <libcontract_priv.h> #include <sys/contract/process.h> +#include <sys/vnic.h> +#include <zone.h> #include "dlmgmt_impl.h" typedef enum dlmgmt_db_op { @@ -552,6 +555,10 @@ dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp, linkp->ll_zoneid, flags, &err)) == NULL) return (err); + /* If transient op and onloan, use the global zone cache file. */ + if (flags == DLMGMT_ACTIVE && linkp->ll_onloan) + req->ls_zoneid = GLOBAL_ZONEID; + /* * If the return error is EINPROGRESS, this request is handled * asynchronously; return success. @@ -714,11 +721,13 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) char attr_name[MAXLINKATTRLEN]; size_t attr_buf_len = 0; void *attr_buf = NULL; + boolean_t rename; curr = buf; len = strlen(buf); attr_name[0] = '\0'; for (i = 0; i < len; i++) { + rename = B_FALSE; char c = buf[i]; boolean_t match = (c == '=' || (c == ',' && !found_type) || c == ';'); @@ -768,6 +777,21 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) goto parse_fail; linkp->ll_media = (uint32_t)*(int64_t *)attr_buf; + } else if (strcmp(attr_name, "zone") == 0) { + if (read_str(curr, &attr_buf) == 0) + goto parse_fail; + linkp->ll_zoneid = getzoneidbyname(attr_buf); + if (linkp->ll_zoneid == -1) { + if (errno == EFAULT) + abort(); + /* + * If we can't find the zone, assign the + * link to the GZ and mark it for being + * renamed. + */ + linkp->ll_zoneid = 0; + rename = B_TRUE; + } } else { attr_buf_len = translators[type].read_func(curr, &attr_buf); @@ -811,6 +835,16 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr); } + + /* + * The zone that this link belongs to has died, we are + * reparenting it to the GZ and renaming it to avoid name + * collisions. + */ + if (rename == B_TRUE) { + (void) snprintf(linkp->ll_link, MAXLINKNAMELEN, + "SUNWorphan%u", (uint16_t)(gethrtime() / 1000)); + } curr = buf + i + 1; } @@ -1222,12 +1256,19 @@ generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link); if (!persist) { + char zname[ZONENAME_MAX]; /* - * We store the linkid in the active database so that dlmgmtd - * can recover in the event that it is restarted. + * We store the linkid and the zone name in the active database + * so that dlmgmtd can recover in the event that it is + * restarted. */ u64 = linkp->ll_linkid; ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64); + + if (getzonenamebyid(linkp->ll_zoneid, zname, + sizeof (zname)) != -1) { + ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname); + } } u64 = linkp->ll_class; ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); @@ -1382,38 +1423,88 @@ dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func) } /* + * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture. + * + * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to + * dlmgmt_zfop() which tries to fork, enter the zone and read the file. + * Because of the upcall architecture of dlmgmtd this can lead to deadlock + * with the following scenario: + * a) the thread preparing to fork will have acquired the malloc locks + * then attempt to suspend every thread in preparation to fork. + * b) all of the upcalls will be blocked in door_ucred() trying to malloc() + * and get the credentials of their caller. + * c) we can't suspend the in-kernel thread making the upcall. + * + * Thus, we cannot serve door requests because we're blocked in malloc() + * which fork() owns, but fork() is in turn blocked on the in-kernel thread + * making the door upcall. This is a fundamental architectural problem with + * any server handling upcalls and also trying to fork(). + * + * To minimize the chance of this deadlock occuring, we check ahead of time to + * see if the file we want to read actually exists in the zone (which it almost + * never does), so we don't need fork in that case (i.e. rarely to never). + */ +static boolean_t +zone_file_exists(char *zoneroot, char *filename) +{ + struct stat sb; + char fname[MAXPATHLEN]; + + (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename); + + if (stat(fname, &sb) == -1) + return (B_FALSE); + + return (B_TRUE); +} + +/* * Initialize the datalink <link name, linkid> mapping and the link's * attributes list based on the configuration file /etc/dladm/datalink.conf * and the active configuration cache file * /etc/svc/volatile/dladm/datalink-management:default.cache. */ int -dlmgmt_db_init(zoneid_t zoneid) +dlmgmt_db_init(zoneid_t zoneid, char *zoneroot) { dlmgmt_db_req_t *req; int err; boolean_t boot = B_FALSE; + char tdir[MAXPATHLEN]; + char *path = cachefile; if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL, DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL) return (err); - if ((err = dlmgmt_process_db_req(req)) != 0) { - /* - * If we get back ENOENT, that means that the active - * configuration file doesn't exist yet, and is not an error. - * We'll create it down below after we've loaded the - * persistent configuration. - */ - if (err != ENOENT) - goto done; + /* Handle running in a non-native branded zone (i.e. has /native) */ + if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) { + (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile); + path = tdir; + } + + if (zone_file_exists(zoneroot, path)) { + if ((err = dlmgmt_process_db_req(req)) != 0) { + /* + * If we get back ENOENT, that means that the active + * configuration file doesn't exist yet, and is not an + * error. We'll create it down below after we've + * loaded the persistent configuration. + */ + if (err != ENOENT) + goto done; + boot = B_TRUE; + } + } else { boot = B_TRUE; } - req->ls_flags = DLMGMT_PERSIST; - err = dlmgmt_process_db_req(req); - if (err != 0 && err != ENOENT) - goto done; + if (zone_file_exists(zoneroot, DLMGMT_PERSISTENT_DB_PATH)) { + req->ls_flags = DLMGMT_PERSIST; + err = dlmgmt_process_db_req(req); + if (err != 0 && err != ENOENT) + goto done; + } err = 0; if (rewrite_needed) { /* @@ -1442,6 +1533,11 @@ done: /* * Remove all links in the given zoneid. + * + * We do this work in two different passes. In the first pass, we remove any + * entry that hasn't been loaned and mark every entry that has been loaned as + * something that is going to be tombstomed. In the second pass, we drop the + * table lock for every entry and remove the tombstombed entry for our zone. */ void dlmgmt_db_fini(zoneid_t zoneid) @@ -1451,9 +1547,66 @@ dlmgmt_db_fini(zoneid_t zoneid) while (linkp != NULL) { next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); if (linkp->ll_zoneid == zoneid) { - (void) dlmgmt_destroy_common(linkp, - DLMGMT_ACTIVE | DLMGMT_PERSIST); + boolean_t onloan = linkp->ll_onloan; + + /* + * Cleanup any VNICs that were loaned to the zone + * before the zone goes away and we can no longer + * refer to the VNIC by the name/zoneid. + */ + if (onloan) { + (void) dlmgmt_delete_db_entry(linkp, + DLMGMT_ACTIVE); + linkp->ll_tomb = B_TRUE; + } else { + (void) dlmgmt_destroy_common(linkp, + DLMGMT_ACTIVE | DLMGMT_PERSIST); + } + } linkp = next_linkp; } + +again: + linkp = avl_first(&dlmgmt_name_avl); + while (linkp != NULL) { + vnic_ioc_delete_t ioc; + + next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); + + if (linkp->ll_zoneid != zoneid) { + linkp = next_linkp; + continue; + } + ioc.vd_vnic_id = linkp->ll_linkid; + if (linkp->ll_tomb != B_TRUE) + abort(); + + /* + * We have to drop the table lock while going up into the + * kernel. If we hold the table lock while deleting a vnic, we + * may get blocked on the mac perimeter and the holder of it may + * want something from dlmgmtd. + */ + dlmgmt_table_unlock(); + + if (ioctl(dladm_dld_fd(dld_handle), + VNIC_IOC_DELETE, &ioc) < 0) + dlmgmt_log(LOG_WARNING, "dlmgmt_db_fini " + "delete VNIC ioctl failed %d %d", + ioc.vd_vnic_id, errno); + + /* + * Even though we've dropped the lock, we know that nothing else + * could have removed us. Therefore, it should be safe to go + * through and delete ourselves, but do nothing else. We'll have + * to restart iteration from the beginning. This can be painful. + */ + dlmgmt_table_lock(B_TRUE); + + (void) dlmgmt_destroy_common(linkp, + DLMGMT_ACTIVE | DLMGMT_PERSIST); + goto again; + } + } diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c index 11e4329669..137c2a6fb3 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_door.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_door.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ /* @@ -58,6 +59,10 @@ #include <libsysevent.h> #include <libdlmgmt.h> #include <librcm.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #include "dlmgmt_impl.h" typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t, @@ -379,6 +384,11 @@ dlmgmt_upcall_destroy(void *argp, void *retp, size_t *sz, zoneid_t zoneid, if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) goto done; + if (linkp->ll_tomb == B_TRUE) { + err = EINPROGRESS; + goto done; + } + if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) { if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0) goto done; @@ -439,6 +449,10 @@ dlmgmt_getlinkid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, dlmgmt_link_t *linkp; int err = 0; + /* Enable the global zone to lookup links it has given away. */ + if (zoneid == GLOBAL_ZONEID && getlinkid->ld_zoneid != -1) + zoneid = getlinkid->ld_zoneid; + /* * Hold the reader lock to access the link */ @@ -648,6 +662,12 @@ dlmgmt_remapid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) goto done; + if (linkp->ll_tomb == B_TRUE) { + err = EBUSY; + goto done; + } + + if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) { err = EEXIST; goto done; @@ -709,6 +729,11 @@ dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) goto done; + if (linkp->ll_tomb == B_TRUE) { + err = EBUSY; + goto done; + } + if (linkp->ll_flags & DLMGMT_ACTIVE) { err = EINVAL; goto done; @@ -1216,6 +1241,11 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) goto done; + if (linkp->ll_tomb == B_TRUE) { + err = EBUSY; + goto done; + } + /* We can only assign an active link to a zone. */ if (!(linkp->ll_flags & DLMGMT_ACTIVE)) { err = EINVAL; @@ -1245,7 +1275,19 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, "zone %d: %s", linkid, oldzoneid, strerror(err)); goto done; } - avl_remove(&dlmgmt_loan_avl, linkp); + + if (newzoneid == GLOBAL_ZONEID && linkp->ll_onloan) { + /* + * We can only reassign a loaned VNIC back to the + * global zone when the zone is shutting down, since + * otherwise the VNIC is in use by the zone and will be + * busy. Leave the VNIC assigned to the zone so we can + * still see it and delete it when dlmgmt_zonehalt() + * runs. + */ + goto done; + } + linkp->ll_onloan = B_FALSE; } if (newzoneid != GLOBAL_ZONEID) { @@ -1256,7 +1298,6 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, (void) zone_add_datalink(oldzoneid, linkid); goto done; } - avl_add(&dlmgmt_loan_avl, linkp); linkp->ll_onloan = B_TRUE; } @@ -1309,6 +1350,10 @@ dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid, int err = 0; dlmgmt_door_zonehalt_t *zonehalt = argp; dlmgmt_zonehalt_retval_t *retvalp = retp; + static char my_pid[10]; + + if (my_pid[0] == NULL) + (void) snprintf(my_pid, sizeof (my_pid), "%d\n", getpid()); if ((err = dlmgmt_checkprivs(0, cred)) == 0) { if (zoneid != GLOBAL_ZONEID) { @@ -1316,9 +1361,26 @@ dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid, } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) { err = EINVAL; } else { + /* + * dls and mac don't honor the locking rules defined in + * mac. In order to try and make that case less likely + * to happen, we try to serialize some of the zone + * activity here between dlmgmtd and the brands on + * /etc/dladm/zone.lck + */ + int fd; + + while ((fd = open(ZONE_LOCK, O_WRONLY | + O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0) + (void) sleep(1); + (void) write(fd, my_pid, sizeof (my_pid)); + (void) close(fd); + dlmgmt_table_lock(B_TRUE); dlmgmt_db_fini(zonehalt->ld_zoneid); dlmgmt_table_unlock(); + + (void) unlink(ZONE_LOCK); } } retvalp->lr_err = err; diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h index cdfd0d8a4d..dde27ef66e 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h +++ b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ /* @@ -67,6 +68,7 @@ typedef struct dlmgmt_link_s { avl_node_t ll_loan_node; uint32_t ll_flags; uint32_t ll_gen; /* generation number */ + boolean_t ll_tomb; /* tombstombed */ } dlmgmt_link_t; /* @@ -84,6 +86,8 @@ typedef struct dlmgmt_dlconf_s { avl_node_t ld_node; } dlmgmt_dlconf_t; +#define ZONE_LOCK "/etc/dladm/zone.lck" + extern boolean_t debug; extern const char *progname; extern char cachefile[]; @@ -138,7 +142,7 @@ void dlmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t); void dlmgmt_log(int, const char *, ...); int dlmgmt_write_db_entry(const char *, dlmgmt_link_t *, uint32_t); int dlmgmt_delete_db_entry(dlmgmt_link_t *, uint32_t); -int dlmgmt_db_init(zoneid_t); +int dlmgmt_db_init(zoneid_t, char *); void dlmgmt_db_fini(zoneid_t); #ifdef __cplusplus diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_main.c b/usr/src/cmd/dlmgmtd/dlmgmt_main.c index c02610bb5f..d8397cc0e6 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_main.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_main.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2014 Joyent, Inc. All rights reserved. */ /* @@ -125,15 +126,24 @@ dlmgmt_door_fini(void) dlmgmt_door_fd = -1; } -int +static int dlmgmt_door_attach(zoneid_t zoneid, char *rootdir) { int fd; int err = 0; char doorpath[MAXPATHLEN]; + struct stat statbuf; - (void) snprintf(doorpath, sizeof (doorpath), "%s%s", rootdir, - DLMGMT_DOOR); + /* Handle running in a non-native branded zone (i.e. has /native) */ + (void) snprintf(doorpath, sizeof (doorpath), "%s/native%s", + rootdir, DLMGMT_TMPFS_DIR); + if (stat(doorpath, &statbuf) == 0) { + (void) snprintf(doorpath, sizeof (doorpath), "%s/native%s", + rootdir, DLMGMT_DOOR); + } else { + (void) snprintf(doorpath, sizeof (doorpath), "%s%s", + rootdir, DLMGMT_DOOR); + } /* * Create the door file for dlmgmtd. @@ -192,8 +202,16 @@ dlmgmt_zone_init(zoneid_t zoneid) (void) snprintf(tmpfsdir, sizeof (tmpfsdir), "%s%s", rootdir, DLMGMT_TMPFS_DIR); if (stat(tmpfsdir, &statbuf) < 0) { - if (mkdir(tmpfsdir, (mode_t)0755) < 0) - return (errno); + if (mkdir(tmpfsdir, (mode_t)0755) < 0) { + /* + * Handle running in a non-native branded zone + * (i.e. has /native) + */ + (void) snprintf(tmpfsdir, sizeof (tmpfsdir), + "%s/native%s", rootdir, DLMGMT_TMPFS_DIR); + if (mkdir(tmpfsdir, (mode_t)0755) < 0) + return (errno); + } } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { return (ENOTDIR); } @@ -203,7 +221,7 @@ dlmgmt_zone_init(zoneid_t zoneid) return (EPERM); } - if ((err = dlmgmt_db_init(zoneid)) != 0) + if ((err = dlmgmt_db_init(zoneid, rootdir)) != 0) return (err); return (dlmgmt_door_attach(zoneid, rootdir)); } @@ -214,7 +232,7 @@ dlmgmt_zone_init(zoneid_t zoneid) static int dlmgmt_allzones_init(void) { - int err, i; + int i; zoneid_t *zids = NULL; uint_t nzids, nzids_saved; @@ -235,11 +253,37 @@ again: } for (i = 0; i < nzids; i++) { - if ((err = dlmgmt_zone_init(zids[i])) != 0) - break; + int res; + zone_status_t status; + + /* + * Skip over zones that have gone away or are going down + * since we got the list. Process all zones in the list, + * logging errors for any that failed. + */ + if (zone_getattr(zids[i], ZONE_ATTR_STATUS, &status, + sizeof (status)) < 0) + continue; + switch (status) { + case ZONE_IS_SHUTTING_DOWN: + case ZONE_IS_EMPTY: + case ZONE_IS_DOWN: + case ZONE_IS_DYING: + case ZONE_IS_DEAD: + /* FALLTHRU */ + continue; + default: + break; + } + if ((res = dlmgmt_zone_init(zids[i])) != 0) { + (void) fprintf(stderr, "zone (%ld) init error %s", + zids[i], strerror(res)); + dlmgmt_log(LOG_ERR, "zone (%d) init error %s", + zids[i], strerror(res)); + } } free(zids); - return (err); + return (0); } static int @@ -262,6 +306,8 @@ dlmgmt_init(void) return (err); } + (void) unlink(ZONE_LOCK); + /* * First derive the name of the cache file from the FMRI name. This * cache name is used to keep active datalink configuration. diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_util.c b/usr/src/cmd/dlmgmtd/dlmgmt_util.c index afcfbed37b..7493ee3577 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_util.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_util.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ /* @@ -45,13 +46,10 @@ /* * There are three datalink AVL tables. The dlmgmt_name_avl tree contains all * datalinks and is keyed by zoneid and link name. The dlmgmt_id_avl also - * contains all datalinks, and it is keyed by link ID. The dlmgmt_loan_avl is - * keyed by link name, and contains the set of global-zone links that are - * currently on loan to non-global zones. + * contains all datalinks, and it is keyed by link ID. */ avl_tree_t dlmgmt_name_avl; avl_tree_t dlmgmt_id_avl; -avl_tree_t dlmgmt_loan_avl; avl_tree_t dlmgmt_dlconf_avl; @@ -162,8 +160,6 @@ dlmgmt_linktable_init(void) offsetof(dlmgmt_link_t, ll_name_node)); avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t), offsetof(dlmgmt_link_t, ll_id_node)); - avl_create(&dlmgmt_loan_avl, cmp_link_by_name, sizeof (dlmgmt_link_t), - offsetof(dlmgmt_link_t, ll_loan_node)); avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id, sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node)); dlmgmt_nextlinkid = 1; @@ -181,7 +177,6 @@ dlmgmt_linktable_fini(void) avl_destroy(&dlmgmt_dlconf_avl); avl_destroy(&dlmgmt_name_avl); - avl_destroy(&dlmgmt_loan_avl); avl_destroy(&dlmgmt_id_avl); } @@ -385,7 +380,6 @@ link_activate(dlmgmt_link_t *linkp) linkp->ll_zoneid = zoneid; avl_add(&dlmgmt_name_avl, linkp); - avl_add(&dlmgmt_loan_avl, linkp); linkp->ll_onloan = B_TRUE; } } else if (linkp->ll_zoneid != GLOBAL_ZONEID) { @@ -430,10 +424,6 @@ link_by_name(const char *name, zoneid_t zoneid) (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); link.ll_zoneid = zoneid; linkp = avl_find(&dlmgmt_name_avl, &link, NULL); - if (linkp == NULL && zoneid == GLOBAL_ZONEID) { - /* The link could be on loan to a non-global zone? */ - linkp = avl_find(&dlmgmt_loan_avl, &link, NULL); - } return (linkp); } @@ -461,6 +451,7 @@ dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media, linkp->ll_linkid = dlmgmt_nextlinkid; linkp->ll_zoneid = zoneid; linkp->ll_gen = 0; + linkp->ll_tomb = B_FALSE; if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL || avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) { @@ -511,8 +502,6 @@ dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) { (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid); - if (linkp->ll_onloan) - avl_remove(&dlmgmt_loan_avl, linkp); } if (linkp->ll_flags == 0) { diff --git a/usr/src/cmd/dlmgmtd/svc-dlmgmtd b/usr/src/cmd/dlmgmtd/svc-dlmgmtd index 7559207535..a75e71f9b3 100644 --- a/usr/src/cmd/dlmgmtd/svc-dlmgmtd +++ b/usr/src/cmd/dlmgmtd/svc-dlmgmtd @@ -23,17 +23,16 @@ # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012 Joyent, Inc. All rights reserved. # -# ident "%Z%%M% %I% %E% SMI" . /lib/svc/share/smf_include.sh -# The real daemon is not started in a non-global zone. But we need to -# create a dummy background process to preserve contract lifetime. +# The real daemon is not started in a non-global zone. Exit to leave +# an empty contract. if smf_is_nonglobalzone; then - (while true ; do sleep 3600 ; done) & - exit $SMF_EXIT_OK + exit $SMF_EXIT_NODAEMON fi # Start the dlmgmtd daemon. diff --git a/usr/src/cmd/dlstat/dlstat.c b/usr/src/cmd/dlstat/dlstat.c index a931ba82ff..2615fdbb12 100644 --- a/usr/src/cmd/dlstat/dlstat.c +++ b/usr/src/cmd/dlstat/dlstat.c @@ -62,7 +62,7 @@ typedef struct link_chain_s { struct link_chain_s *lc_next; } link_chain_t; -typedef void * (*stats2str_t)(const char *, void *, +typedef void * (*stats2str_t)(const char *, const char *, void *, char, boolean_t); typedef struct show_state { @@ -141,6 +141,7 @@ typedef struct total_fields_buf_s { char t_rbytes[MAXSTATLEN]; char t_opackets[MAXSTATLEN]; char t_obytes[MAXSTATLEN]; + char t_zone[ZONENAME_MAX]; } total_fields_buf_t; static ofmt_field_t total_s_fields[] = { @@ -154,6 +155,8 @@ static ofmt_field_t total_s_fields[] = { offsetof(total_fields_buf_t, t_opackets), print_default_cb}, { "OBYTES", 8, offsetof(total_fields_buf_t, t_obytes), print_default_cb}, +{ "ZONE", 20, + offsetof(total_fields_buf_t, t_zone), print_default_cb}, { NULL, 0, 0, NULL}}; /* @@ -957,8 +960,8 @@ cleanup_removed_links(show_state_t *state) } void * -print_total_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_total_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { total_stat_entry_t *sentry = statentry; total_stat_t *link_stats = &sentry->tse_stats; @@ -970,6 +973,7 @@ print_total_stats(const char *linkname, void *statentry, char unit, (void) snprintf(buf->t_linkname, sizeof (buf->t_linkname), "%s", linkname); + (void) snprintf(buf->t_zone, sizeof (buf->t_zone), "%s", zonename); map_to_units(buf->t_ipackets, sizeof (buf->t_ipackets), link_stats->ts_ipackets, unit, parsable); @@ -988,8 +992,8 @@ done: } void * -print_rx_generic_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_generic_ring_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1022,8 +1026,8 @@ done: } void * -print_tx_generic_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_generic_ring_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1056,8 +1060,8 @@ done: } void * -print_rx_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_ring_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1090,8 +1094,8 @@ done: } void * -print_tx_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_ring_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1124,8 +1128,8 @@ done: } void * -print_rx_generic_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_generic_lane_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { rx_lane_stat_entry_t *sentry = statentry; rx_lane_stat_t *link_stats = &sentry->rle_stats; @@ -1172,8 +1176,8 @@ done: } void * -print_tx_generic_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_generic_lane_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { tx_lane_stat_entry_t *sentry = statentry; tx_lane_stat_t *link_stats = &sentry->tle_stats; @@ -1217,8 +1221,8 @@ done: } void * -print_rx_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_lane_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { rx_lane_stat_entry_t *sentry = statentry; rx_lane_stat_t *link_stats = &sentry->rle_stats; @@ -1283,9 +1287,8 @@ done: } void * -print_tx_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) -{ +print_tx_lane_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { tx_lane_stat_entry_t *sentry = statentry; tx_lane_stat_t *link_stats = &sentry->tle_stats; tx_lane_fields_buf_t *buf = NULL; @@ -1338,8 +1341,8 @@ done: } void * -print_fanout_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_fanout_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { fanout_stat_entry_t *sentry = statentry; fanout_stat_t *link_stats = &sentry->fe_stats; @@ -1392,8 +1395,8 @@ done: } void * -print_aggr_port_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_aggr_port_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { aggr_port_stat_entry_t *sentry = statentry; aggr_port_stat_t *link_stats = &sentry->ape_stats; @@ -1470,7 +1473,8 @@ done: void walk_dlstat_stats(show_state_t *state, const char *linkname, - dladm_stat_type_t stattype, dladm_stat_chain_t *diff_stat) + const char *zonename, dladm_stat_type_t stattype, + dladm_stat_chain_t *diff_stat) { dladm_stat_chain_t *curr; @@ -1480,7 +1484,8 @@ walk_dlstat_stats(show_state_t *state, const char *linkname, /* Format the raw numbers for printing */ fields_buf = state->ls_stats2str[stattype](linkname, - curr->dc_statentry, state->ls_unit, state->ls_parsable); + zonename, curr->dc_statentry, state->ls_unit, + state->ls_parsable); /* Print the stats */ if (fields_buf != NULL) ofmt_print(state->ls_ofmt, fields_buf); @@ -1495,12 +1500,20 @@ show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg) int i; dladm_stat_chain_t *diff_stat; char linkname[DLPI_LINKNAME_MAX]; + char zonename[DLADM_PROP_VAL_MAX + 1]; + char *valptr[1]; + uint_t valcnt = 1; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { goto done; } + valptr[0] = zonename; + if (dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_CURRENT, "zone", + (char **)valptr, &valcnt) != 0) + zonename[0] = '\0'; + for (i = 0; i < DLADM_STAT_NUM_STATS; i++) { if (state->ls_stattype[i]) { /* @@ -1508,7 +1521,8 @@ show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg) * Stats are returned as chain of raw numbers */ diff_stat = query_link_stats(handle, linkid, arg, i); - walk_dlstat_stats(state, linkname, i, diff_stat); + walk_dlstat_stats(state, linkname, zonename, i, + diff_stat); dladm_link_stat_free(diff_stat); } } @@ -1628,7 +1642,7 @@ do_show(int argc, char *argv[], const char *use) char *o_fields_str = NULL; char *total_stat_fields = - "link,ipkts,rbytes,opkts,obytes"; + "link,ipkts,rbytes,opkts,obytes,zone"; char *rx_total_stat_fields = "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50"; char *tx_total_stat_fields = diff --git a/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile b/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile index 99a571bee8..7aa028b4c1 100644 --- a/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile +++ b/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile @@ -23,7 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" PROG = jdtrace SRCS = jdtrace.c @@ -77,7 +76,7 @@ $(PROG): $(SRCS) JFLAGS= -g -cp $(CLASSPATH) -d $(CLASSDIR) -deprecation JFLAGS += -target 1.5 -JFLAGS += -Xlint +JFLAGS += -Xlint -Xlint:-path COMPILE.java=$(JAVAC) $(JFLAGS) JAVASRC= JDTrace.java Getopt.java diff --git a/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl b/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl index d6f1c8c277..e7f9189822 100644 --- a/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl +++ b/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl @@ -566,7 +566,7 @@ $defdir = -d $dt_tst ? $dt_tst : '.'; $bindir = -d $dt_bin ? $dt_bin : '.'; if (!$opt_F) { - my @dependencies = ("gcc", "make", "java", "perl"); + my @dependencies = ("gcc", "cc", "make", "java", "perl", "printenv"); for my $dep (@dependencies) { if (!inpath($dep)) { diff --git a/usr/src/cmd/dtrace/test/tst/common/Makefile b/usr/src/cmd/dtrace/test/tst/common/Makefile index fa3a8b17c0..95ada26513 100644 --- a/usr/src/cmd/dtrace/test/tst/common/Makefile +++ b/usr/src/cmd/dtrace/test/tst/common/Makefile @@ -152,6 +152,14 @@ usdt/tst.forker.o: usdt/forker.h usdt/forker.h: usdt/forker.d $(DTRACE) -h -s usdt/forker.d -o usdt/forker.h +ustack/tst.unpriv.exe: ustack/tst.unpriv.o ustack/unpriv_helper.o + $(LINK.c) -o ustack/tst.unpriv.exe \ + ustack/tst.unpriv.o ustack/unpriv_helper.o $(LDLIBS) + $(POST_PROCESS) ; $(STRIP_STABS) + +ustack/unpriv_helper.o: ustack/unpriv_helper.d + $(COMPILE.d) -o ustack/unpriv_helper.o -s ustack/unpriv_helper.d + usdt/tst.lazyprobe.exe: usdt/tst.lazyprobe.o usdt/lazyprobe.o $(LINK.c) -o usdt/tst.lazyprobe.exe \ usdt/tst.lazyprobe.o usdt/lazyprobe.o $(LDLIBS) diff --git a/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile b/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile index fe213dd22a..26bf656edc 100644 --- a/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile +++ b/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile @@ -23,7 +23,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" include $(SRC)/Makefile.master @@ -62,7 +61,7 @@ install: all $(PROTO_TEST_JAR) JFLAGS= -g -cp $(CLASSPATH) -d $(CLASSDIR) -deprecation JFLAGS += -target 1.5 -JFLAGS += -Xlint +JFLAGS += -Xlint -Xlint:-path COMPILE.java=$(JAVAC) $(JFLAGS) $(TEST_JAR): $(SRCDIR)/*.java diff --git a/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d b/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d index e2882b3f8e..a9138d2f54 100644 --- a/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d +++ b/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d @@ -19,10 +19,6 @@ * CDDL HEADER END */ -/* - * Copyright (c) 2011, Joyent, Inc. All rights reserved. - */ - #pragma D option quiet BEGIN diff --git a/usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh b/usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh new file mode 100644 index 0000000000..c5921b8d28 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh @@ -0,0 +1,69 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +DIR=/var/tmp/dtest.$$ + +mkdir $DIR +cd $DIR + +expected=`od -t u8 -N 8 /dev/urandom | head -1 | cut -d ' ' -f2` + +$dtrace -x bufpolicy=ring -x bufsize=10k -qs /dev/stdin > /dev/null 2>&1 <<EOF & + tick-1ms + /i < 10000/ + { + printf("%d: expected is $expected!\n", i++); + } + + tick-1ms + /i >= 10000/ + { + exit(0); + } +EOF + +background=$! + +# +# Give some time for the enabling to get there... +# +sleep 2 + +echo "::walk dtrace_state | ::dtrace" | mdb -k | tee test.out +grep "expected is $expected" test.out 2> /dev/null 1>&2 +status=$? + +kill $background + +cd / +/usr/bin/rm -rf $DIR + +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c new file mode 100644 index 0000000000..43ba244444 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c @@ -0,0 +1,7 @@ +int +main(int argc, char *argv[]) +{ + for (;;) + ; + return (0); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh new file mode 100644 index 0000000000..26c430bff7 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh @@ -0,0 +1,68 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +ppriv -s A=basic,dtrace_user,dtrace_proc $$ + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +file=out.$$ +dtrace=$1 + +rm -f $file + +dir=`/bin/dirname $tst` + +$dtrace -o $file -c $dir/tst.unpriv.exe -ws /dev/stdin <<EOF + profile-1234hz + /pid == \$target/ + { + @[ustack(20, 8192)] = count(); + } + + tick-1s + { + secs++; + } + + tick-1s + /secs > 10/ + { + trace("test timed out"); + exit(1); + } + + profile-1234hz + /pid == \$target && secs > 5/ + { + raise(SIGINT); + exit(0); + } +EOF + +status=$? +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d b/usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d new file mode 100644 index 0000000000..eb7b0e9e9d --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d @@ -0,0 +1,4 @@ +dtrace:helper:ustack: +{ + this->otherstr = "doogle"; +} diff --git a/usr/src/cmd/flowadm/flowadm.c b/usr/src/cmd/flowadm/flowadm.c index 374fa1675c..34e597dc78 100644 --- a/usr/src/cmd/flowadm/flowadm.c +++ b/usr/src/cmd/flowadm/flowadm.c @@ -21,6 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -233,9 +234,9 @@ usage(void) (void) fprintf(stderr, gettext("usage: flowadm <subcommand>" " <args>...\n" " add-flow [-t] -l <link> -a <attr>=<value>[,...]\n" - "\t\t [-p <prop>=<value>,...] <flow>\n" - " remove-flow [-t] {-l <link> | <flow>}\n" - " show-flow [-p] [-l <link>] " + "\t\t [-p <prop>=<value>,...] [-z zonename] <flow>\n" + " remove-flow [-t] [-z zonename] {-l <link> | <flow>}\n" + " show-flow [-p] [-l <link>] [-z zonename] " "[<flow>]\n\n" " set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n" " reset-flowprop [-t] [-p <prop>,...] <flow>\n" @@ -333,11 +334,12 @@ do_add_flow(int argc, char *argv[]) dladm_arg_list_t *proplist = NULL; dladm_arg_list_t *attrlist = NULL; dladm_status_t status; + char *zonename = NULL; bzero(propstr, DLADM_STRSIZE); bzero(attrstr, DLADM_STRSIZE); - while ((option = getopt_long(argc, argv, "tR:l:a:p:", + while ((option = getopt_long(argc, argv, "tR:l:a:p:z:", prop_longopts, NULL)) != -1) { switch (option) { case 't': @@ -351,9 +353,6 @@ do_add_flow(int argc, char *argv[]) MAXLINKNAMELEN) >= MAXLINKNAMELEN) { die("link name too long"); } - if (dladm_name2info(handle, devname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) - die("invalid link '%s'", devname); l_arg = B_TRUE; break; case 'a': @@ -368,6 +367,9 @@ do_add_flow(int argc, char *argv[]) DLADM_STRSIZE) die("property list too long '%s'", propstr); break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option); } @@ -376,6 +378,10 @@ do_add_flow(int argc, char *argv[]) die("link is required"); } + if (dladm_zname2info(handle, zonename, devname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) + die("invalid link '%s'", devname); + opterr = 0; index = optind; @@ -414,11 +420,12 @@ do_remove_flow(int argc, char *argv[]) boolean_t l_arg = B_FALSE; remove_flow_state_t state; dladm_status_t status; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":tR:l:", + while ((option = getopt_long(argc, argv, ":tR:l:z:", longopts, NULL)) != -1) { switch (option) { case 't': @@ -432,12 +439,11 @@ do_remove_flow(int argc, char *argv[]) MAXLINKNAMELEN) >= MAXLINKNAMELEN) { die("link name too long"); } - if (dladm_name2info(handle, linkname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) { - die("invalid link '%s'", linkname); - } l_arg = B_TRUE; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option); break; @@ -458,6 +464,12 @@ do_remove_flow(int argc, char *argv[]) /* if link is specified then flow name should not be there */ if (optind == argc-1) usage(); + + if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) { + die("invalid link '%s'", linkname); + } + /* walk the link to find flows and remove them */ state.fs_tempop = t_arg; state.fs_altroot = altroot; @@ -597,11 +609,12 @@ do_show_flow(int argc, char *argv[]) ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPl:o:", + while ((option = getopt_long(argc, argv, ":pPl:o:z:", longopts, NULL)) != -1) { switch (option) { case 'p': @@ -622,17 +635,23 @@ do_show_flow(int argc, char *argv[]) if (strlcpy(linkname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long\n"); - if (dladm_name2info(handle, linkname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) - die("invalid link '%s'", linkname); l_arg = B_TRUE; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option); break; } } + if (l_arg) { + if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) + die("invalid link '%s'", linkname); + } + /* get flow name (optional last argument */ if (optind == (argc-1)) { if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) diff --git a/usr/src/cmd/flowstat/flowstat.c b/usr/src/cmd/flowstat/flowstat.c index 3ddff9e34f..781ce6b0f0 100644 --- a/usr/src/cmd/flowstat/flowstat.c +++ b/usr/src/cmd/flowstat/flowstat.c @@ -21,6 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -190,9 +191,9 @@ static char *progname; static dladm_handle_t handle = NULL; const char *usage_ermsg = "flowstat [-r | -t] [-i interval] " - "[-l link] [flow]\n" + "[-l link] [-z zonename] [flow]\n" " flowstat [-S] [-A] [-i interval] [-p] [ -o field[,...]]\n" - " [-u R|K|M|G|T|P] [-l link] [flow]\n" + " [-u R|K|M|G|T|P] [-l link] [-z zonename] [flow]\n" " flowstat -h [-a] [-d] [-F format]" " [-s <DD/MM/YYYY,HH:MM:SS>]\n" " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> " @@ -556,6 +557,7 @@ main(int argc, char *argv[]) show_flow_state_t state; char *fields_str = NULL; char *o_fields_str = NULL; + char *zonename = NULL; char *total_stat_fields = "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs"; @@ -582,10 +584,11 @@ main(int argc, char *argv[]) if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) die_dlerr(status, "could not open /dev/dld"); + linkname[0] = '\0'; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:h", + while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:hz:", NULL, NULL)) != -1) { switch (option) { case 'r': @@ -642,9 +645,6 @@ main(int argc, char *argv[]) if (strlcpy(linkname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long\n"); - if (dladm_name2info(handle, linkname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) - die("invalid link '%s'", linkname); break; case 'h': if (r_arg || t_arg || p_arg || o_arg || u_arg || @@ -655,6 +655,9 @@ main(int argc, char *argv[]) do_show_history(argc, argv); return (0); break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, usage_ermsg); break; @@ -683,6 +686,12 @@ main(int argc, char *argv[]) die("the option -A is not compatible with " "-r, -t, -p, -o, -u, -i"); + if (linkname[0] != '\0') { + if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) + die("invalid link '%s'", linkname); + } + /* get flow name (optional last argument) */ if (optind == (argc-1)) { if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) diff --git a/usr/src/cmd/fs.d/Makefile b/usr/src/cmd/fs.d/Makefile index 8e8faaa643..6cf7642d17 100644 --- a/usr/src/cmd/fs.d/Makefile +++ b/usr/src/cmd/fs.d/Makefile @@ -19,6 +19,7 @@ # CDDL HEADER END # # Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2012, Joyent, Inc. All rights reserved. # # The filesystem independent utilities clri, fsdb, dcopy, labelit, and mkfs @@ -38,8 +39,8 @@ DEFAULTFILES= fs.dfl include ../Makefile.cmd -SUBDIR1= lofs zfs -SUBDIR2= dev fd pcfs nfs hsfs proc ctfs udfs ufs tmpfs cachefs \ +SUBDIR1= bootfs hyprlofs lofs zfs +SUBDIR2= dev fd pcfs nfs hsfs lxproc proc ctfs udfs ufs tmpfs cachefs \ autofs mntfs objfs sharefs smbclnt reparsed SUBDIRS= $(SUBDIR1) $(SUBDIR2) I18NDIRS= $(SUBDIR2) diff --git a/usr/src/cmd/fs.d/bootfs/Makefile b/usr/src/cmd/fs.d/bootfs/Makefile new file mode 100644 index 0000000000..d0ac4311f4 --- /dev/null +++ b/usr/src/cmd/fs.d/bootfs/Makefile @@ -0,0 +1,21 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +FSTYPE= bootfs +LIBPROG= mount + +include ../Makefile.fstype +include ../Makefile.mount +include ../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/bootfs/mount.c b/usr/src/cmd/fs.d/bootfs/mount.c new file mode 100644 index 0000000000..5363a4f872 --- /dev/null +++ b/usr/src/cmd/fs.d/bootfs/mount.c @@ -0,0 +1,139 @@ +/* + * 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. + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include <errno.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/mount.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <fslib.h> + +#define MNTTYPE_BOOTFS "bootfs" + +static char optbuf[MAX_MNTOPT_STR] = { '\0', }; +static int optsize = 0; + +static void +usage(void) +{ + (void) fprintf(stderr, + "Usage: mount [-Ormq] [-o options] special mountpoint\n"); + exit(2); +} + +/* + * usage: mount [-Ormq] [-o options] special mountp + * + * This mount program is exec'ed by /usr/sbin/mount if '-F bootfs' is + * specified. + */ +int +main(int argc, char *argv[]) +{ + int c; + char *special; /* Entity being mounted */ + char *mountp; /* Entity being mounted on */ + char *savedoptbuf; + char *myname; + char typename[64]; + int flags = 0; + int errflag = 0; + int qflg = 0; + + myname = strrchr(argv[0], '/'); + myname = myname ? myname+1 : argv[0]; + (void) snprintf(typename, sizeof (typename), "%s %s", MNTTYPE_BOOTFS, + myname); + argv[0] = typename; + + while ((c = getopt(argc, argv, "o:rmOq")) != EOF) { + switch (c) { + case '?': + errflag++; + break; + + case 'o': + if (strlcpy(optbuf, optarg, sizeof (optbuf)) >= + sizeof (optbuf)) { + (void) fprintf(stderr, + gettext("%s: Invalid argument: %s\n"), + myname, optarg); + return (2); + } + optsize = strlen(optbuf); + break; + case 'O': + flags |= MS_OVERLAY; + break; + case 'r': + flags |= MS_RDONLY; + break; + + case 'm': + flags |= MS_NOMNTTAB; + break; + + case 'q': + qflg = 1; + break; + + default: + usage(); + } + } + if ((argc - optind != 2) || errflag) { + usage(); + } + special = argv[argc - 2]; + mountp = argv[argc - 1]; + + if ((savedoptbuf = strdup(optbuf)) == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + myname); + exit(2); + } + + if (mount(special, mountp, flags | MS_OPTIONSTR, MNTTYPE_BOOTFS, NULL, + 0, optbuf, MAX_MNTOPT_STR)) { + (void) fprintf(stderr, "mount: "); + perror(special); + exit(2); + } + if (optsize && !qflg) { + cmp_requested_to_actual_options(savedoptbuf, optbuf, + special, mountp); + } + + return (0); +} diff --git a/usr/src/cmd/fs.d/hyprlofs/Makefile b/usr/src/cmd/fs.d/hyprlofs/Makefile new file mode 100644 index 0000000000..1a3aaf18d3 --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/Makefile @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved +# + +SUBDIRS= hlcfg mount + +all:= TARGET= all +install:= TARGET= install +clean:= TARGET= clean +clobber:= TARGET= clobber +lint:= TARGET= lint + +.KEEP_STATE: + +.PARALLEL: $(SUBDIRS) + +all install clean clobber lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile b/usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile new file mode 100644 index 0000000000..d2ae22e9fd --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2012, Joyent, Inc. All rights reserved. +# + +FSTYPE= hyprlofs +LIBPROG= hlcfg + +include ../../Makefile.fstype +include ../../Makefile.mount +include ../../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c b/usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c new file mode 100644 index 0000000000..16e8e32b1c --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c @@ -0,0 +1,244 @@ +/* + * 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 2012, Joyent, Inc. All rights reserved. + */ + +/* + * This is a simple test program to exercise the hyprlofs ioctls. This is + * not designed as a full featured CLI and only does minimal error checking + * and reporting. + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libgen.h> +#include <strings.h> +#include <sys/errno.h> +#include <sys/fs/hyprlofs.h> + +extern int errno; + +char *usage = "usage: <fs path> add [<file name> <alias>]+\n" + " <fs path> addl [<file name>]+\n" + " <fs path> rm [<alias>]+\n" + " <fs path> clear" + " <fs path> get"; + +typedef enum { + CMD_ADD, + CMD_RM, + CMD_CLR, + CMD_ADDL, + CMD_GET +} cmd_t; + +static int +get_entries(int fd) +{ + int err; + int i; + hyprlofs_curr_entries_t e; + hyprlofs_curr_entry_t *ep; + + e.hce_cnt = 0; + e.hce_entries = NULL; + + err = ioctl(fd, HYPRLOFS_GET_ENTRIES, &e); + if (err != 0 && errno != E2BIG) { + perror("ioctl"); + return (1); + } + + if (err == 0) { + (void) printf("success, but no entries\n"); + return (0); + } + + /* + * E2BIG is what we expect when there are existing mappings + * since the current cnt is still returned in that case. + */ + (void) printf("cnt: %d\n", e.hce_cnt); + + /* alloc array and call again, then print array */ + if ((ep = (hyprlofs_curr_entry_t *) + malloc(sizeof (hyprlofs_curr_entry_t) * e.hce_cnt)) == NULL) { + (void) fprintf(stderr, "out of memory\n"); + exit(1); + } + + e.hce_entries = ep; + errno = 0; + if (ioctl(fd, HYPRLOFS_GET_ENTRIES, &e) != 0) { + /* + * Not handling an increase here. We would need to free and + * start over to do that, but ok for a test program. + */ + perror("ioctl"); + free(ep); + return (1); + } + for (i = 0; i < e.hce_cnt; i++) + (void) printf("%s %s\n", ep[i].hce_path, ep[i].hce_name); + + free(ep); + return (0); +} + +int +main(int argc, char **argv) +{ + int i, ap; + cmd_t cmd; + int cnt = 0; + int fd; + int rv = 0; + hyprlofs_entry_t *e = NULL; + hyprlofs_entries_t ents; + + if (argc < 3) { + (void) fprintf(stderr, "%s\n", usage); + exit(1); + } + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + perror("can't open hyprlofs mount"); + exit(1); + } + + if (strcmp(argv[2], "add") == 0) { + cmd = CMD_ADD; + } else if (strcmp(argv[2], "rm") == 0) { + cmd = CMD_RM; + } else if (strcmp(argv[2], "clear") == 0) { + cmd = CMD_CLR; + } else if (strcmp(argv[2], "addl") == 0) { + cmd = CMD_ADDL; + } else if (strcmp(argv[2], "get") == 0) { + cmd = CMD_GET; + } else { + (void) fprintf(stderr, "%s\n", usage); + exit(1); + } + + /* Count up the number of parameters. The arg format varies w/ cmd */ + switch (cmd) { + case CMD_ADD: + for (i = 3; i < argc; i++) { + /* argv[i] is the file path */ + + /* The next arg is the alias */ + if (++i >= argc) { + (void) fprintf(stderr, "missing alias for %s\n", + argv[i - 1]); + exit(1); + } + + cnt++; + } + break; + case CMD_ADDL: + cnt = argc - 3; + break; + case CMD_RM: + cnt = argc - 3; + break; + case CMD_CLR: /*FALLTHRU*/ + case CMD_GET: + if (argc > 3) { + (void) fprintf(stderr, "%s\n", usage); + exit(1); + } + break; + } + + if (cnt > 0) { + if ((e = (hyprlofs_entry_t *)malloc(sizeof (hyprlofs_entry_t) * + cnt)) == NULL) { + (void) fprintf(stderr, "out of memory\n"); + exit(1); + } + } + + /* + * Format up the args. + * We only setup the path member for the add cmd. + * We won't run this loop for the clear cmd. + * The addl command is special since we use basename to get the alias. + */ + for (i = 0, ap = 3; i < cnt; i++, ap++) { + if (cmd == CMD_ADDL) { + e[i].hle_path = argv[ap]; + e[i].hle_plen = strlen(e[i].hle_path); + + e[i].hle_name = basename(argv[ap]); + e[i].hle_nlen = strlen(e[i].hle_name); + + continue; + } + + if (cmd == CMD_ADD) { + e[i].hle_path = argv[ap++]; + e[i].hle_plen = strlen(e[i].hle_path); + } + + e[i].hle_name = argv[ap]; + e[i].hle_nlen = strlen(e[i].hle_name); + } + + ents.hle_entries = e; + ents.hle_len = cnt; + + switch (cmd) { + case CMD_ADD: /*FALLTHRU*/ + case CMD_ADDL: + if (ioctl(fd, HYPRLOFS_ADD_ENTRIES, &ents) < 0) { + perror("ioctl"); + rv = 1; + } + break; + case CMD_RM: + if (ioctl(fd, HYPRLOFS_RM_ENTRIES, &ents) < 0) { + perror("ioctl"); + rv = 1; + } + break; + case CMD_CLR: + if (ioctl(fd, HYPRLOFS_RM_ALL) < 0) { + perror("ioctl"); + rv = 1; + } + break; + case CMD_GET: + rv = get_entries(fd); + break; + } + + (void) close(fd); + if (cnt > 0) + free(e); + return (rv); +} diff --git a/usr/src/cmd/fs.d/hyprlofs/mount/Makefile b/usr/src/cmd/fs.d/hyprlofs/mount/Makefile new file mode 100644 index 0000000000..a0b63d211c --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/mount/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2012 Joyent, Inc. All rights reserved. +# + +FSTYPE= hyprlofs +LIBPROG= mount + +include ../../Makefile.fstype +include ../../Makefile.mount +include ../../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/hyprlofs/mount/mount.c b/usr/src/cmd/fs.d/hyprlofs/mount/mount.c new file mode 100644 index 0000000000..a95c9ca3c2 --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/mount/mount.c @@ -0,0 +1,148 @@ +/* + * 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. + * Copyright 2012 Joyent, Inc. All rights reserved. + */ + +#define HLFS +#define MNTTYPE_HYFS "hyprlofs" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include <errno.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/mount.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <fslib.h> + +#define RET_OK 0 +/* + * /sbin/mount and the fs-local method understand this exit code to + * mean that all the mount failures were related to hyprlofs mounts. Since + * this program only attempts to mount hyfs file systems, when it fails + * it returns this exit status. + */ +#define RET_ERR 111 + +static void usage(void); + +static char optbuf[MAX_MNTOPT_STR] = { '\0', }; +static int optsize = 0; + +static char fstype[] = MNTTYPE_HYFS; + +/* + * usage: mount [-Ormq] [-o options] special mountp + * + * This mount program is exec'ed by /usr/sbin/mount if '-F hyprlofs' is + * specified. + */ +int +main(int argc, char *argv[]) +{ + int c; + char *special; /* Entity being mounted */ + char *mountp; /* Entity being mounted on */ + char *savedoptbuf; + char *myname; + char typename[64]; + int flags = 0; + int errflag = 0; + int qflg = 0; + + myname = strrchr(argv[0], '/'); + myname = myname ? myname+1 : argv[0]; + (void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname); + argv[0] = typename; + + while ((c = getopt(argc, argv, "o:rmOq")) != EOF) { + switch (c) { + case '?': + errflag++; + break; + + case 'o': + if (strlcpy(optbuf, optarg, sizeof (optbuf)) >= + sizeof (optbuf)) { + (void) fprintf(stderr, + gettext("%s: Invalid argument: %s\n"), + myname, optarg); + return (2); + } + optsize = strlen(optbuf); + break; + case 'O': + flags |= MS_OVERLAY; + break; + case 'r': + flags |= MS_RDONLY; + break; + + case 'm': + flags |= MS_NOMNTTAB; + break; + + case 'q': + qflg = 1; + break; + + default: + usage(); + } + } + if ((argc - optind != 2) || errflag) { + usage(); + } + special = argv[argc - 2]; + mountp = argv[argc - 1]; + + if ((savedoptbuf = strdup(optbuf)) == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + myname); + exit(2); + } + if (mount(special, mountp, flags | MS_OPTIONSTR, fstype, NULL, 0, + optbuf, MAX_MNTOPT_STR)) { + (void) fprintf(stderr, "mount: "); + perror(special); + exit(RET_ERR); + } + if (optsize && !qflg) + cmp_requested_to_actual_options(savedoptbuf, optbuf, + special, mountp); + return (0); +} + +void +usage(void) +{ + (void) fprintf(stderr, + "Usage: mount [-Ormq] [-o options] special mountpoint\n"); + exit(RET_ERR); +} diff --git a/usr/src/cmd/fs.d/lxproc/Makefile b/usr/src/cmd/fs.d/lxproc/Makefile new file mode 100644 index 0000000000..77075ef1dd --- /dev/null +++ b/usr/src/cmd/fs.d/lxproc/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +FSTYPE= lxproc +LIBPROG= mount + +include ../Makefile.fstype +include ../Makefile.mount +include ../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/lxproc/mount.c b/usr/src/cmd/fs.d/lxproc/mount.c new file mode 100644 index 0000000000..5a000997bd --- /dev/null +++ b/usr/src/cmd/fs.d/lxproc/mount.c @@ -0,0 +1,140 @@ +/* + * 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. + * Copyright 2012 Joyent, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include <errno.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/mount.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <fslib.h> + +#define RET_OK 0 +#define RET_ERR 33 + +static void usage(void); + +static char optbuf[MAX_MNTOPT_STR] = { '\0', }; +static int optsize = 0; + +static char fstype[] = "lxproc"; + +/* + * usage: mount [-Ormq] [-o options] special mountp + * + * This mount program is exec'ed by /usr/sbin/mount if '-F lxproc' is + * specified. + */ +int +main(int argc, char *argv[]) +{ + int c; + char *special; /* Entity being mounted */ + char *mountp; /* Entity being mounted on */ + char *savedoptbuf; + char *myname; + char typename[64]; + int flags = 0; + int errflag = 0; + int qflg = 0; + + myname = strrchr(argv[0], '/'); + myname = myname ? myname+1 : argv[0]; + (void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname); + argv[0] = typename; + + while ((c = getopt(argc, argv, "o:rmOq")) != EOF) { + switch (c) { + case '?': + errflag++; + break; + + case 'o': + if (strlcpy(optbuf, optarg, sizeof (optbuf)) >= + sizeof (optbuf)) { + (void) fprintf(stderr, + gettext("%s: Invalid argument: %s\n"), + myname, optarg); + return (2); + } + optsize = strlen(optbuf); + break; + case 'O': + flags |= MS_OVERLAY; + break; + case 'r': + flags |= MS_RDONLY; + break; + + case 'm': + flags |= MS_NOMNTTAB; + break; + + case 'q': + qflg = 1; + break; + + default: + usage(); + } + } + if ((argc - optind != 2) || errflag) { + usage(); + } + special = argv[argc - 2]; + mountp = argv[argc - 1]; + + if ((savedoptbuf = strdup(optbuf)) == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + myname); + exit(2); + } + if (mount(special, mountp, flags | MS_OPTIONSTR, fstype, NULL, 0, + optbuf, MAX_MNTOPT_STR)) { + (void) fprintf(stderr, "mount: "); + perror(special); + exit(RET_ERR); + } + if (optsize && !qflg) + cmp_requested_to_actual_options(savedoptbuf, optbuf, + special, mountp); + return (0); +} + +void +usage(void) +{ + (void) fprintf(stderr, + "Usage: mount [-Ormq] [-o options] special mountpoint\n"); + exit(RET_ERR); +} diff --git a/usr/src/cmd/fs.d/nfs/lib/smfcfg.c b/usr/src/cmd/fs.d/nfs/lib/smfcfg.c index ba2420362a..b79fff4125 100644 --- a/usr/src/cmd/fs.d/nfs/lib/smfcfg.c +++ b/usr/src/cmd/fs.d/nfs/lib/smfcfg.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> @@ -344,8 +345,23 @@ fs_smf_get_prop(smf_fstype_t fstype, char *prop_name, char *cbuf, } else { ret = scf_error(); } - if ((ret != 0) && scf_error() != SCF_ERROR_NONE) - fprintf(stdout, gettext("%s\n"), scf_strerror(ret)); + if ((ret != 0) && scf_error() != SCF_ERROR_NONE) { + /* + * This is a workaround for the NFS service manifests not + * containing the proper properties in local zones. + * + * When in a local zone and the property doesn't exist on an NFS + * service (most likely nfs/server or nfs/client), don't print + * the error. The caller will still see the correct error code, + * but a user creating a delegated dataset or mounting an NFS + * share won't see this spurious error. + */ + if (getzoneid() == GLOBAL_ZONEID || + scf_error() != SCF_ERROR_NOT_FOUND) { + fprintf(stdout, gettext("%s\n"), scf_strerror(ret)); + } + } + out: fs_smf_fini(phandle); return (ret); diff --git a/usr/src/cmd/fs.d/nfs/lib/smfcfg.h b/usr/src/cmd/fs.d/nfs/lib/smfcfg.h index c06327d801..f0b70907aa 100644 --- a/usr/src/cmd/fs.d/nfs/lib/smfcfg.h +++ b/usr/src/cmd/fs.d/nfs/lib/smfcfg.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #ifndef _SMFCFG_H @@ -42,6 +43,7 @@ #include <locale.h> #include <errno.h> #include <sys/types.h> +#include <zone.h> #ifdef __cplusplus extern "C" { diff --git a/usr/src/cmd/fs.d/nfs/mount/Makefile b/usr/src/cmd/fs.d/nfs/mount/Makefile index dad33922a3..c2485208a8 100644 --- a/usr/src/cmd/fs.d/nfs/mount/Makefile +++ b/usr/src/cmd/fs.d/nfs/mount/Makefile @@ -19,18 +19,12 @@ # CDDL HEADER END # # Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014, Joyent, Inc. All rights reserved. # # cmd/fs.d/nfs/mount/Makefile FSTYPE= nfs LIBPROG= mount -ROOTFS_PROG= $(LIBPROG) - -# duplicate ROOTLIBFSTYPE value needed for installation rule -# we must define this before including Makefile.fstype -ROOTLIBFSTYPE = $(ROOT)/usr/lib/fs/$(FSTYPE) -$(ROOTLIBFSTYPE)/%: $(ROOTLIBFSTYPE) % - $(RM) $@; $(SYMLINK) ../../../../etc/fs/$(FSTYPE)/$(LIBPROG) $@ include ../../Makefile.fstype @@ -65,7 +59,7 @@ CLOBBERFILES += $(LIBPROG) .KEEP_STATE: -all: $(ROOTFS_PROG) +all: $(LIBPROG) $(LIBPROG): webnfs.h $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) @@ -114,7 +108,9 @@ $(POFILE): $(SRCS) sed "/^domain/d" messages.po > $@ $(RM) $(POFILE).i messages.po -install: $(ROOTETCPROG) +install: all $(FSTYPEPROG) + $(RM) $(ROOTETCPROG) + $(SYMLINK) ../../../usr/lib/fs/$(FSTYPE)/$(LIBPROG) $(ROOTETCPROG) lint: webnfs.h webnfs_xdr.c webnfs_client.c lint_SRCS diff --git a/usr/src/cmd/fs.d/nfs/mount/mount.c b/usr/src/cmd/fs.d/nfs/mount/mount.c index c228a91d05..b3b255d098 100644 --- a/usr/src/cmd/fs.d/nfs/mount/mount.c +++ b/usr/src/cmd/fs.d/nfs/mount/mount.c @@ -2104,7 +2104,7 @@ get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp, } while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers, - vers_min, vers_to_try, "datagram_v")) == NULL) { + vers_min, vers_to_try, NULL)) == NULL) { if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) { pr_err(gettext("%s: %s\n"), fshost, clnt_spcreateerror("")); diff --git a/usr/src/cmd/fs.d/nfs/mountd/mountd.c b/usr/src/cmd/fs.d/nfs/mountd/mountd.c index 0d9ac76d30..666ad427d5 100644 --- a/usr/src/cmd/fs.d/nfs/mountd/mountd.c +++ b/usr/src/cmd/fs.d/nfs/mountd/mountd.c @@ -22,6 +22,7 @@ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Joyent, Inc. All rights reserved. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ @@ -419,6 +420,13 @@ main(int argc, char *argv[]) exit(1); } + /* Mountd cannot run in a non-global zone. */ + if (getzoneid() != GLOBAL_ZONEID) { + (void) fprintf(stderr, "%s: can only run in the global zone\n", + argv[0]); + exit(1); + } + if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { syslog(LOG_ERR, "getrlimit failed"); } else { diff --git a/usr/src/cmd/fs.d/nfs/nfsd/nfsd.c b/usr/src/cmd/fs.d/nfs/nfsd/nfsd.c index 6c0e0bda5e..c34c39a13e 100644 --- a/usr/src/cmd/fs.d/nfs/nfsd/nfsd.c +++ b/usr/src/cmd/fs.d/nfs/nfsd/nfsd.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -176,6 +177,13 @@ main(int ac, char *av[]) exit(1); } + /* Nfsd cannot run in a non-global zone. */ + if (getzoneid() != GLOBAL_ZONEID) { + (void) fprintf(stderr, "%s: can only run in the global zone\n", + av[0]); + exit(1); + } + (void) enable_extended_FILE_stdio(-1, -1); /* diff --git a/usr/src/cmd/fs.d/nfs/svc/nfs-server b/usr/src/cmd/fs.d/nfs/svc/nfs-server index b0c6d155a5..8542ce9b9f 100644 --- a/usr/src/cmd/fs.d/nfs/svc/nfs-server +++ b/usr/src/cmd/fs.d/nfs/svc/nfs-server @@ -50,13 +50,13 @@ configure_ipfilter() # # Nothing to do if: + # - service's policy is 'use_global' # - ipfilter isn't online # - global policy is 'custom' - # - service's policy is 'use_global' # + [ "`get_policy $SMF_FMRI`" = "use_global" ] && return 0 service_check_state $IPF_FMRI $SMF_ONLINE || return 0 [ "`get_global_def_policy`" = "custom" ] && return 0 - [ "`get_policy $SMF_FMRI`" = "use_global" ] && return 0 svcadm restart $IPF_FMRI } diff --git a/usr/src/cmd/fs.d/nfs/umount/umount.c b/usr/src/cmd/fs.d/nfs/umount/umount.c index aabdc8a592..66d280bcdb 100644 --- a/usr/src/cmd/fs.d/nfs/umount/umount.c +++ b/usr/src/cmd/fs.d/nfs/umount/umount.c @@ -297,7 +297,7 @@ retry: */ timep = (quick ? &create_timeout : NULL); cl = clnt_create_timed(list[i].host, MOUNTPROG, vers, - "datagram_n", timep); + NULL, timep); /* * Do not print any error messages in case of forced * unmount. diff --git a/usr/src/cmd/halt/halt.c b/usr/src/cmd/halt/halt.c index 36867c88dc..3b57e02c64 100644 --- a/usr/src/cmd/halt/halt.c +++ b/usr/src/cmd/halt/halt.c @@ -21,6 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. @@ -1236,6 +1237,17 @@ do_archives_update(int do_fast_reboot) pid_t pid; char *cmd_argv[MAXARGS]; +#if defined(__i386) + { + /* + * bootadm will complain and exit if not a grub root, so + * just skip running it. + */ + struct stat sb; + if (stat("/boot/grub/stage2", &sb) == -1) + return; + } +#endif /* __i386 */ cmd_argv[i++] = "/sbin/bootadm"; cmd_argv[i++] = "-ea"; @@ -1300,7 +1312,7 @@ main(int argc, char *argv[]) optstring = "dlnqfp"; usage = gettext("usage: %s [ -dlnq(p|f) ] [ boot args ]\n"); #endif - cmd = A_SHUTDOWN; + cmd = A_REBOOT; fcn = AD_BOOT; } else { (void) fprintf(stderr, @@ -1498,7 +1510,8 @@ main(int argc, char *argv[]) * check_zone_haltedness later on. */ if (zoneid == GLOBAL_ZONEID && cmd != A_DUMP) { - need_check_zones = halt_zones(); + if (!qflag) + need_check_zones = halt_zones(); } #if defined(__i386) @@ -1598,7 +1611,7 @@ main(int argc, char *argv[]) (void) signal(SIGINT, SIG_IGN); - if (!qflag && !nosync) { + if (!nosync) { struct utmpx wtmpx; bzero(&wtmpx, sizeof (struct utmpx)); diff --git a/usr/src/cmd/ibd_upgrade/ibd_delete_link.c b/usr/src/cmd/ibd_upgrade/ibd_delete_link.c index b9d10a56cd..f63630207d 100644 --- a/usr/src/cmd/ibd_upgrade/ibd_delete_link.c +++ b/usr/src/cmd/ibd_upgrade/ibd_delete_link.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #include <stdio.h> @@ -86,6 +87,7 @@ ibd_delete_link(dladm_handle_t dlh, char *link) getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID; (void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN); + getlinkid.ld_zoneid = -1; if ((status = ibd_dladm_door_call(dlh, &getlinkid, sizeof (getlinkid), &retval, sizeof (retval))) != DLADM_STATUS_OK) { diff --git a/usr/src/cmd/init/Makefile b/usr/src/cmd/init/Makefile index a867a0e780..b4b857882b 100644 --- a/usr/src/cmd/init/Makefile +++ b/usr/src/cmd/init/Makefile @@ -25,11 +25,13 @@ # PROG= init +OBJS= init.o ROOTFS_PROG= $(PROG) DEFAULTFILES= init.dfl include ../Makefile.cmd +include ../Makefile.ctf LDLIBS += -lpam -lbsm -lcontract -lscf CERRWARN += -_gcc=-Wno-parentheses @@ -41,6 +43,10 @@ CLOBBERFILES= $(STATIC) all: $(ROOTFS_PROG) +$(ROOTFS_PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + install: all $(ROOTETCDEFAULTFILES) $(ROOTSBINPROG) $(RM) $(ROOTETCPROG) $(RM) $(ROOTUSRSBINPROG) @@ -58,4 +64,8 @@ clean: lint: lint_PROG +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + include ../Makefile.targ diff --git a/usr/src/cmd/init/init.c b/usr/src/cmd/init/init.c index 031a053b65..f6f8bccdbb 100644 --- a/usr/src/cmd/init/init.c +++ b/usr/src/cmd/init/init.c @@ -23,6 +23,7 @@ * Copyright (c) 2013 Gary Mills * * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -144,6 +145,8 @@ #define UT_USER_SZ 32 /* Size of a utmpx ut_user field */ #define UT_LINE_SZ 32 /* Size of a utmpx ut_line field */ +#define CHECK_SVC SCF_INSTANCE_FS_MINIMAL + /* * SLEEPTIME The number of seconds "init" sleeps between wakeups if * nothing else requires this "init" wakeup. @@ -696,9 +699,8 @@ main(int argc, char *argv[]) console(B_FALSE, "\n\n%s Release %s Version %s %d-bit\r\n", un.sysname, un.release, un.version, bits); - console(B_FALSE, - "Copyright (c) 1983, 2010, Oracle and/or its affiliates." - " All rights reserved.\r\n"); + console(B_FALSE, "Copyright (c) 2010-2012, " + "Joyent Inc. All rights reserved.\r\n"); } /* @@ -3509,6 +3511,28 @@ bail: } /* + * Attempt to confirm that svc.startd is ready to accept a user-initiated + * run-level change. startd is not ready until it has started its + * _scf_notify_wait thread to watch for events from svc.configd. This is + * inherently racy. To workaround this, we check the status of a file that + * startd will create once it has started the _scf_notify_wait thread. + * If we don't see this file after one minute, then charge ahead. + */ +static void +verify_startd_ready() +{ + struct stat64 buf; + int i; + + for (i = 0; i < 60; i++) { + if (stat64("/etc/svc/volatile/startd.ready", &buf) == 0) + return; + sleep(1); + } + console(B_TRUE, "verify startd timeout\n"); +} + +/* * Function to handle requests from users to main init running as process 1. */ static void @@ -3596,6 +3620,12 @@ userinit(int argc, char **argv) (void) audit_put_record(ADT_SUCCESS, ADT_SUCCESS, argv[1]); /* + * Before we tell init to start a run-level change, we need to be + * sure svc.startd is ready to accept that. + */ + verify_startd_ready(); + + /* * Signal init; init will take care of telling svc.startd. */ if (kill(init_pid, init_signal) == FAILURE) { @@ -4305,9 +4335,7 @@ contract_event(struct pollfd *poll) if (ret == 0) { if (cookie == STARTD_COOKIE && do_restart_startd) { - if (smf_debug) - console(B_TRUE, "Restarting " - "svc.startd.\n"); + console(B_TRUE, "Restarting svc.startd.\n"); /* * Account for the failure. If the failure rate diff --git a/usr/src/cmd/initpkg/mountall.sh b/usr/src/cmd/initpkg/mountall.sh index 56f2798e18..38693dd8da 100644 --- a/usr/src/cmd/initpkg/mountall.sh +++ b/usr/src/cmd/initpkg/mountall.sh @@ -28,6 +28,8 @@ # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# usage () { if [ -n "$1" ]; then @@ -148,6 +150,9 @@ isremote() { # Get list of remote FS types (just once) RemoteFSTypes=`while read t junk; do echo $t; done < /etc/dfs/fstypes` +# Ensure nfs/smbfs are remote FS types even if not delivered from fstypes +isremote "nfs" || set -A RemoteFSTypes "nfs" +isremote "smbfs" || set -A RemoteFSTypes "smbfs" # # Process command line args diff --git a/usr/src/cmd/initpkg/shutdown.sh b/usr/src/cmd/initpkg/shutdown.sh index e65224c632..820d50310c 100644 --- a/usr/src/cmd/initpkg/shutdown.sh +++ b/usr/src/cmd/initpkg/shutdown.sh @@ -41,7 +41,7 @@ usage() { } notify() { - /usr/sbin/wall -a <<-! + /usr/sbin/wall -Za <<-! $* ! if [ -x /usr/sbin/showmount -a -x /usr/sbin/rwall ] diff --git a/usr/src/cmd/initpkg/umountall.sh b/usr/src/cmd/initpkg/umountall.sh index c9a94fd8f1..4a45e19e18 100644 --- a/usr/src/cmd/initpkg/umountall.sh +++ b/usr/src/cmd/initpkg/umountall.sh @@ -25,6 +25,7 @@ # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # usage () { @@ -98,6 +99,9 @@ isremote() { # Get list of remote FS types (just once) RemoteFSTypes=`while read t junk; do echo $t; done < /etc/dfs/fstypes` +# Ensure nfs/smbfs are remote FS types even if not delivered from fstypes +isremote "nfs" || set -A RemoteFSTypes "nfs" +isremote "smbfs" || set -A RemoteFSTypes "smbfs" # # Process command line args diff --git a/usr/src/cmd/ipf/tools/Makefile.tools b/usr/src/cmd/ipf/tools/Makefile.tools index 7c1e151762..ce0db79970 100644 --- a/usr/src/cmd/ipf/tools/Makefile.tools +++ b/usr/src/cmd/ipf/tools/Makefile.tools @@ -23,7 +23,6 @@ # Use is subject to license terms. # # Copyright 2013 Nexenta Systems, Inc. All rights reserved. -# # Copyright (c) 2012, Joyent Inc. All rights reserved. # diff --git a/usr/src/cmd/ipf/tools/ipfstat.c b/usr/src/cmd/ipf/tools/ipfstat.c index fd39556465..f50b722bde 100644 --- a/usr/src/cmd/ipf/tools/ipfstat.c +++ b/usr/src/cmd/ipf/tools/ipfstat.c @@ -165,6 +165,10 @@ static int sort_dstip __P((const void *, const void *)); static int sort_dstpt __P((const void *, const void *)); #endif +#if SOLARIS +#include "ipfzone.h" +#endif + static void usage(name) char *name; diff --git a/usr/src/cmd/krb5/kadmin/Makefile b/usr/src/cmd/krb5/kadmin/Makefile index de893f05a4..8d0dbdc93f 100644 --- a/usr/src/cmd/krb5/kadmin/Makefile +++ b/usr/src/cmd/krb5/kadmin/Makefile @@ -25,7 +25,7 @@ include ../../Makefile.cmd -SUBDIRS= cli dbutil ktutil kpasswd server kclient kdcmgr gui +SUBDIRS= cli dbutil ktutil kpasswd server kclient kdcmgr all := TARGET= all clean := TARGET= clean diff --git a/usr/src/cmd/ksh/Makefile.com b/usr/src/cmd/ksh/Makefile.com index 8716f4eb9d..e5615cb407 100644 --- a/usr/src/cmd/ksh/Makefile.com +++ b/usr/src/cmd/ksh/Makefile.com @@ -36,11 +36,13 @@ LIBSHELLBASE=../../../lib/libshell LIBSHELLSRC=$(LIBSHELLBASE)/common/sh SRCS= $(OBJECTS:%.o=$(LIBSHELLSRC)/%.c) +OBJS= $(OBJECTS) LDLIBS += -lshell # Set common AST build flags (e.g., needed to support the math stuff). include ../../../Makefile.ast +include ../../Makefile.ctf # 1. Make sure that the -D/-U defines in CFLAGS below are in sync # with usr/src/lib/libshell/Makefile.com diff --git a/usr/src/cmd/localedef/Makefile b/usr/src/cmd/localedef/Makefile index 4b012d0c45..ad0a46f5ed 100644 --- a/usr/src/cmd/localedef/Makefile +++ b/usr/src/cmd/localedef/Makefile @@ -13,6 +13,7 @@ # Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright 2011 EveryCity Ltd. All rights reserved. # Copyright 2013 DEY Storage Systems, Inc. +# Copyright 2012 Joyent, Inc. All rights reserved. # PROG=localedef @@ -51,13 +52,13 @@ POFILE = localedef_cmd.po ISO8859_1_LOCALES = \ da_DK \ - de_CH \ + de_CH de_DE \ en_AU en_CA en_GB en_NZ en_US \ - es_AR es_BO es_CL es_CO es_EC es_GT es_MX es_NI es_PA \ + es_AR es_BO es_CL es_CO es_EC es_ES es_GT es_MX es_NI es_PA \ es_PE es_SV es_UY es_VE \ - fr_CA fr_CH \ + fr_CA fr_CH fr_FR \ is_IS \ - it_CH \ + it_CH it_IT \ sv_SE ISO8859_2_LOCALES = \ @@ -275,8 +276,11 @@ locale/%.UTF-8/stamp: data/%.UTF-8.src UTF-8.cm \ $(SED) '/^LC_CTYPE/,/^END LC_CTYPE/d;$$r UTF-8.ct' $< | \ ./$(PROG) -U -w data/widths.txt -f UTF-8.cm $(@D) $(TOUCH) $@ +# Convert EURO_SIGN to CURRENCY_SIGN for the ISO8859-1 locales locale/%.ISO8859-1/stamp: data/%.UTF-8.src 8859-1.cm locale $(PROG) - ./$(PROG) -U -w data/widths.txt -i $< -f 8859-1.cm $(@D) + sed 's/EURO_SIGN/CURRENCY_SIGN/' $< > $<.tmp + ./$(PROG) -U -w data/widths.txt -i $<.tmp -f 8859-1.cm $(@D) + rm -f $<.tmp $(TOUCH) $@ locale/%.ISO8859-2/stamp: data/%.UTF-8.src 8859-2.cm locale $(PROG) ./$(PROG) -U -w data/widths.txt -i $< -f 8859-2.cm $(@D) diff --git a/usr/src/cmd/lofiadm/main.c b/usr/src/cmd/lofiadm/main.c index 0740bce1b7..0c32b69323 100644 --- a/usr/src/cmd/lofiadm/main.c +++ b/usr/src/cmd/lofiadm/main.c @@ -349,7 +349,8 @@ out: * DO NOT use this function if the filename is actually the device name. */ static int -lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename) +lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename, + boolean_t no_devlink_flag) { int minor; @@ -362,7 +363,8 @@ lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename) "unsupported")); die(gettext("could not map file %s"), filename); } - wait_until_dev_complete(minor); + if (!no_devlink_flag) + wait_until_dev_complete(minor); return (minor); } @@ -372,7 +374,8 @@ lofi_map_file(int lfd, struct lofi_ioctl li, const char *filename) */ static void add_mapping(int lfd, const char *devicename, const char *filename, - mech_alias_t *cipher, const char *rkey, size_t rksz, boolean_t rdonly) + mech_alias_t *cipher, const char *rkey, size_t rksz, + boolean_t rdonly, boolean_t no_devlink_flag) { struct lofi_ioctl li; @@ -408,7 +411,7 @@ add_mapping(int lfd, const char *devicename, const char *filename, int minor; /* pick one via the driver */ - minor = lofi_map_file(lfd, li, filename); + minor = lofi_map_file(lfd, li, filename, no_devlink_flag); /* if mapping succeeds, print the one picked */ (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor); return; @@ -429,7 +432,8 @@ add_mapping(int lfd, const char *devicename, const char *filename, die(gettext("could not map file %s to %s"), filename, devicename); } - wait_until_dev_complete(li.li_minor); + if (!no_devlink_flag) + wait_until_dev_complete(li.li_minor); } /* @@ -1311,7 +1315,7 @@ lofi_uncompress(int lfd, const char *filename) if (statbuf.st_size == 0) return; - minor = lofi_map_file(lfd, li, filename); + minor = lofi_map_file(lfd, li, filename, B_FALSE); (void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d", LOFI_BLOCK_NAME, minor); @@ -1817,6 +1821,7 @@ main(int argc, char *argv[]) boolean_t ephflag = B_FALSE; boolean_t compressflag = B_FALSE; boolean_t uncompressflag = B_FALSE; + boolean_t no_devlink_flag = B_FALSE; /* the next two work together for -c, -k, -T, -e options only */ boolean_t need_crypto = B_FALSE; /* if any -c, -k, -T, -e */ boolean_t cipher_only = B_TRUE; /* if -c only */ @@ -1832,7 +1837,7 @@ main(int argc, char *argv[]) (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); - while ((c = getopt(argc, argv, "a:c:Cd:efk:o:rs:T:U")) != EOF) { + while ((c = getopt(argc, argv, "a:c:Cd:efk:o:rs:T:UX")) != EOF) { switch (c) { case 'a': addflag = B_TRUE; @@ -1910,6 +1915,13 @@ main(int argc, char *argv[]) case 'U': uncompressflag = B_TRUE; break; + case 'X': + /* + * Private flag to skip the wait for the /dev links to + * be created. + */ + no_devlink_flag = B_TRUE; + break; case '?': default: errflag = B_TRUE; @@ -2038,7 +2050,7 @@ main(int argc, char *argv[]) */ if (addflag) add_mapping(lfd, devicename, filename, cipher, rkey, rksz, - rdflag); + rdflag, no_devlink_flag); else if (compressflag) lofi_compress(&lfd, filename, compress_index, segsize); else if (uncompressflag) diff --git a/usr/src/cmd/lp/terminfo/40.ti b/usr/src/cmd/lp/terminfo/40.ti new file mode 100644 index 0000000000..2e08ece1ca --- /dev/null +++ b/usr/src/cmd/lp/terminfo/40.ti @@ -0,0 +1,68 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.4 */ + +###################################################################### +# +# Entries for the AT&T Model 40 line printers +# + + + +40-80-6|att40-80-6|AT&T Model 40 line printer 80 cloumn 6 line per inch, + + daisy, + bufsz#160, + cols#80, + lines#66, + orc#1, + orhi#10, + orl#1, + orvi#6, + cps#400, + + cr=^M, + cuf1=\s, + ff=^L, + +40-80-8|att40-80-8|AT&T Model 40 line printer 80 cloumn 8 line per inch, + + lines#88, + orvi#8, + use=40-80-6, + +40-132-6|att40-132-6|AT&T Model 40 line printer 132 cloumn 6 line per inch, + + bufsz#264, + cols#132, + use=40-80-6, + +40-132-8|att40-132-8|AT&T Model 40 line printer 132 cloumn 8 line per inch, + + lines#88, + orvi#8, + use=40-132-6, + diff --git a/usr/src/cmd/lp/terminfo/44x.ti b/usr/src/cmd/lp/terminfo/44x.ti new file mode 100644 index 0000000000..d932bf441e --- /dev/null +++ b/usr/src/cmd/lp/terminfo/44x.ti @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ + +###################################################################### +# +# Entries for the AT&T 440 seris line printers +# + + +442|att442|AT&T 442 line printer, + + bufsz#2048, + cols#132, + lines#66, + orc#1, + orhi#10, + orl#1, + orvi#6, + cps#440, + + cr=^M, + cuf1=\s, + ff=^L, + +444|att444|AT&T 444 line printer, + + use=442, + +446|att446|AT&T 446 line printer, + + cps#1100, + use=442, + diff --git a/usr/src/cmd/lp/terminfo/45x.ti b/usr/src/cmd/lp/terminfo/45x.ti new file mode 100644 index 0000000000..e4a6c5ab92 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/45x.ti @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ + +###################################################################### +# +# Entries for the AT&T 455, 457, 459 printers +# + +455|att455|AT&T 455 Daisywheel printer, + + use=Gdaisy+basic, + +457|att457|458|att458|AT&T 457 Daisy printer, + + bufsz#1024, + use=455, + + diff --git a/usr/src/cmd/lp/terminfo/477.ti b/usr/src/cmd/lp/terminfo/477.ti new file mode 100644 index 0000000000..51b808b2ce --- /dev/null +++ b/usr/src/cmd/lp/terminfo/477.ti @@ -0,0 +1,181 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.7 */ + +###################################################################### +# +# Entries for the AT&T 477 printer +# + +# +# Basic capabilities (all emulations): +# +att477+basic1, + + bufsz#8192, + cps#80, + +###################################################################### +# +# Particular printers: +# + +477-470|att477-470|AT&T 477; 470 emulation, + + cols#136, + cpix, + + orc#10, + orhi#100, + orl#30, + orvi#180, + + csnm=%?%p1%{0}%=%tamerican%e%p1%{1}%=%tbritish%e%p1%{2}%=%tswedish%e%p1%{3}%=%tgerman%e%p1%{4}%=%tfrench%e%p1%{5}%=%titalian%e%p1%{6}%=%tspanish%;, + scs=%?%p1%{0}%=%t\EZ^O@\ED^B@%e%p1%{1}%=%t\EZ^O@\ED^C@%e%p1%{2}%=%t\EZ^O@\ED^E@%e%p1%{3}%=%t\EZ^O@\ED^D@%e%p1%{4}%=%t\EZ^O@\ED^A@%e%p1%{5}%=%t\EZ^O@\ED^F@%e%p1%{6}%=%t\EZ^O@\ED^G@%;, + + use=470, + +477ibmc|att477ibmc|AT&T 477 IBM Color printer emulation, + + cols#135, + + orc#15, + orhi#150, + + orl#10, + orvi#60, + + cvr=%?%p1%{0}%>%p1%{256}%<%&%t\EA%p1%c\E2%; + + cpi=%?%p1%{10}%=%t^R%e%p1%{12}%=%t\E:%e%p1%{17}%=%t^O%;, + + + smglp=\EX%p1%{1}%+%c%p2%{2}%+%c, + smgt=\E4, + + + use=att477+basic1, use=Gibmc+basic, use=Gibmc+low+5x6, + use=Gibmc+color, + +477ibmg|att477ibmg|AT&T 477 IBM Graphics printer emulation, + + cols#136, + + orc#10, + orhi#100, + orl#10, + orvi#60, + + is2=\E@, + + smglp=%?%p1%{256}%<%t\El%p1%{1}%+%c, + smgrp=%?%p1%{256}%<%t\EQ%p1%{2}%+%c, + + + use=att477+basic1, use=Gibmg+basic, use=Gibmg+low, + +477qume|att477qume|477-455|att477-455|AT&T 477 qume emulation, + + + cols#136, + + + is2=\E\rP\EW\E.\EL08\EY, + + use=att477+basic1, use=Gdaisy+basic, use=Gdaisy+lowres, + +###################################################################### +# +# In Fujitsu DPL24C mode. This seems to be more like the Epson LQ-2500 +# than the IBM Proprinter XL. +# + +# +# Basic capabilities (Fujitsu emulation only): +# +att477+basic2, + + orc#18, + orhi#180, + orl#30, + orvi#180, + + +# +# The following is not redundant with the cpi capability, because +# the cpi changes the character size as well, so that printing +# looks balanced, while this leaves the character size alone. + chr=%?%p1%{256}%<%t\Eh%p1%c%;, + + + is2=\E@, + + csnm=%?%p1%{0}%=%tcharacter_set_1%e%p1%{1}%=%tcharacter_set_2%e%p1%{2}%=%tusa%e%p1%{3}%=%tfrench%e%p1%{4}%=%tgerman%e%p1%{5}%=%tuk%e%p1%{6}%=%tdanish%e%p1%{7}%=%tswedish%e%p1%{8}%=%titalian%e%p1%{9}%=%tspanish%;, + scs=%?%p1%{0}%=%t\E7%e%p1%{1}%=%t\E6%e%p1%{3}%=%t\ER0%e%p1%{3}%=%t\ER1%e%p1%{4}%=%t\ER2%e%p1%{5}%=%t\ER3%e%p1%{6}%=%t\ER4%e%p1%{7}%=%t\ER5%e%p1%{8}%=%t\ER6%e%p1%{9}%=%t\ER7%;, + + +477-5x6|att477-5x6|AT&T 477 as Fujitsu DPL24C; 5:6 aspect ratio, + + spinh#50, + +# defbi= +# Like the defbi for the epson2500, except: +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 50 dots per inch horizontally this means 5 +# dots per character. +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\E3\030^R\EP\EB%p2%{8}%/%c$<>\ED%p1%{6}%/%c$<>\013\r\t%;, + +# +# Note that the epson2500 drives a real Epson LQ-2500 at +# 60 dots per inch horizontally; the same control sequences +# drive the Fujitsu DPL24C at 50 dots per inch horixontally. + use=att477+basic1, use=att477+basic2, use=Gep2500+basic, + use=Gep2500+low, use=Gep2500+color, + +477|att477|AT&T 477 as Fujitsu DPL24C; 1:1; low res, + + is1@, + +# +# This mode differs from the 5x6 mode only in sbim and defbi +# (and spinh, of course). However, it is even closer to the +# epson2500, so we use that. + + sdrfq=\EH, + snlq=\EG, + snrmq@, + + sbim=\E*\005%p1%{256}%m%c%p1%{256}%/%c, + + use=att477+basic1, use=att477+basic2, use=Gep2500+basic, + use=Gep2500+low, use=Gep2500+color, + +477-hi|att477-hi|AT&T 477 as Fujitsu DPL24C; 1:1; high res, + + use=att477+basic1, use=att477+basic2, use=Gep2500+basic, + use=Gep2500+high, use=Gep2500+color, diff --git a/usr/src/cmd/lp/terminfo/47x.ti b/usr/src/cmd/lp/terminfo/47x.ti new file mode 100644 index 0000000000..a5db8850de --- /dev/null +++ b/usr/src/cmd/lp/terminfo/47x.ti @@ -0,0 +1,134 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Entries for the AT&T 470, 471, 473, 474, 475, 476, 478, 479 printers +# + +# +# C.Itoh derived printers: +# + +470|att470|AT&T 470; C.Itoh 8510; 8"; parallel matrix printer, + + bufsz#4096, + + cols#80, + cpix, + + sdrfq@, + snlq@, + snrmq@, + ssubm@, + ssupm@, + rsubm@, + rsupm@, + + sgr0=\E"\EY, + +# Reset command does not reset form length + + is2=\Ec1\Ev66, + + csnm=%?%p1%{0}%=%tusa%e%p1%{1}%=%tbritish%e%p1%{2}%=%tdanish%e%p1%{3}%=%tjapanese%e%p1%{4}%=%tnorwegian%e%p1%{5}%=%tswedish%e%p1%{6}%=%tgerman%e%p1%{7}%=%tfrench%e%p1%{8}%=%tfrench2%e%p1%{9}%=%titalian%e%p1%{10}%=%tspanish%e%p1%{11}%=%tnetherland%e%p1%{12}%=%tafrikaans%e%p1%{13}%=%tbritish2%;, + scs=%?%p1%{0}%=%t\EZ^O@\ED^B@%e%p1%{1}%=%t\EZ^O@\ED^C@%e%p1%{2}%=%t\EZ^O@\ED\b@%e%p1%{3}%=%t\EZ^O@%e%p1%{4}%=%t\EZ^O@\ED\t@%e%p1%{5}%=%t\EZ^O@\ED^E@%e%p1%{6}%=%t\EZ^O@\ED^D@%e%p1%{7}%=%t\EZ^O@\ED^A@%e%p1%{8}%=%t\EZ^O@\ED^N@%e%p1%{9}%=%t\EZ^O@\ED^F@%e%p1%{10}%=%t\EZ^O@\ED^G@%e%p1%{11}%=%t\EZ^O@\ED\n@%e%p1%{12}%=%t\EZ^O@\ED^K@%e%p1%{13}%=%t\ED^O@%;, + + + use=Gcitoh+basic, use=Gcitoh+low, + +471|att471|AT&T 471; C.Itoh 1550; 14"; parallel matrix printer, + + cols#136, + + use=470, + +475|att475|AT&T 475; C.Itoh 8510; 8"; serial matrix printer, + + use=470, + +476|att476|AT&T 475; C.Itoh 1550; 14"; serial matrix printer, + + use=471, + +# +# IBM derived printers: +# + +473|att473|AT&T 473; 8"; C.Itoh 8510EP; IBM Graphics, + + bufsz#4096, + cps#120, + +# +# FIX: The AT&T 473 doesn't seem to have fine-scale horizontal +# motion--the only motion is by columns. + orc#10, + orhi#100, + + + + + use=Gibmg+basic, use=Gibmg+low, + +474|att474|AT&T 474; 14"; C.Itoh 1550EP; IBM Graphics, + + cols#132, + + use=473, + +478|att478|AT&T 478; 8"; parallel matrix printer, + + bufsz#16384, + cps#120, + +# +# FIX: The AT&T 478 doesn't seem to have fine-scale horizontal +# motion--the only motion is by columns. + orc#10, + orhi#100, + + cpi=%?%p1%{10}%=%t^R%e%p1%{12}%=%t\E:%e%p1%{13}%=%p1%{14}%=%O%t\Eh%e%p1%{16}%=%p1%{17}%=%O%t\Em%e%p1%{18}%=%t^O%;, + + is2=^R\EW0\E-0\E_0\EF\EH\EI\ET\EA\014\E2\ER\El\001\Er\120\Et\001\EC\102\E7\EU0\EO, + + sdrfq=\EI^D, + snlq=\EI^B, + + smglp=\El%{1}%p1%+%c, + smgrp=\Er%{1}%p1%+%c, + smgtp=\Et%{1}%p1%+%c, + + use=Gibmg+basic, use=Gibmg+low, + +479|att479|AT&T 479; 14"; IBM parallel; matrix printer, + + cols#132, + is2=^R\EW0\E-0\E_0\EF\EH\EI\004\ET\EA\014\E2\ER\El\001\Er\204\Et\001\EC\102\E7\EU0\EO, + + + use=478, diff --git a/usr/src/cmd/lp/terminfo/495.ti b/usr/src/cmd/lp/terminfo/495.ti new file mode 100644 index 0000000000..562c62b6be --- /dev/null +++ b/usr/src/cmd/lp/terminfo/495.ti @@ -0,0 +1,78 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ + +###################################################################### +# +# Entries for the AT&T 495 printer +# + +495ibm|att495ibm|AT&T 495 IBM Graphics emulation, + + bufsz#1024, + cps#800, + + + orc#12, + orhi#120, + + lines#63, + + cpi=%?%p1%{10}%=%t^R%e%p1%{17}%=%t^O%;, + +# Reset defaults, enter IBM Graphics emulation mode + is2=\E[0~\E[12~, + + use=Gibmg+basic, use=Gibmg+low, + +495qume|att495qume|AT&T 495 Qume emulation, + + daisy@, + + bufsz#1024, + cps#800, + + cols#80, + lines#63, + + + chr=%?%p1%{0}%>%p1%{127}%<%&%t\E^_%p1%{1}%+%c%;, + cvr=%?%p1%{0}%>%p1%{127}%<%&%t\E^^%p1%{1}%+%c%;, + + is2=\E[0~\E[11~\E^_\r, + + u9=%?%p1%{128}%<%t\EF%p1%02d%;, + + use=Gdaisy+basic, use=Gdaisy+lowres, + +495hp|att495hp|AT&T 495 HP Laserjet I emulation, + + bufsz#1024, + cps#800, + + is2=\E[0~\E[10~\E&k0S, + + use=Ghplaser+basic, use=Ghplaser+high, diff --git a/usr/src/cmd/lp/terminfo/53x0.ti b/usr/src/cmd/lp/terminfo/53x0.ti new file mode 100644 index 0000000000..fc6fb2cef9 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/53x0.ti @@ -0,0 +1,81 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Entries for the AT&T 5310, 5320 printers +# + +53x0+high, + + spinv#72, + spinh#150, + + bitwin#2, + u1=2, + +# defbi= +# X is in 1/150 increments; set char spacing to 1/16.7 +# increments to allow us to get close; column is X*16.7/150. +# Y is in 1/144 increments; set line spacing to 1/12 +# increments to allow us to get close; line is Y/12. +# Note: The 5310/5320 won't move upward with the absolute +# addressing control sequence, so we use the relative motion. +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\E[4w\E[%p1%{167}%*%{1500}%/%d`\E[w\E[3z\E[%p2%{12}%/%de\E[z\EP\035q%;, + defbi=%?%p5%{1}%=%t\E[4w\E[%p1%{167}%*%{1500}%/%d`\E[w\E[3z\E[%p2%{12}%/%de\E[z\EP\035q%;, + + use=Gdec+low, + +5320|att5320|AT&T Model 5320 printer (EMUL set to ANSI), + + bufsz#8192, + cps#120, + + +# +# FIX: The AT&T 5320 doesn't seem to have fine-scale horizontal +# motion--the only motion is by columns. + orc#10, + orhi#100, + +# +# FIX: The AT&T 5320 seems to only have half-line vertical motion +# at best. + orl#12, + orvi#72, + + + + use=53x0+high, use=Gdec+basic, use=Gdec+low, + +5310|att5310|AT&T 5310 matrix printer (EMUL set to ANSI), + + cols#80, + + use=5320, diff --git a/usr/src/cmd/lp/terminfo/57x.ti b/usr/src/cmd/lp/terminfo/57x.ti new file mode 100644 index 0000000000..677090111e --- /dev/null +++ b/usr/src/cmd/lp/terminfo/57x.ti @@ -0,0 +1,105 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Entries for the AT&T 570 series printers +# + +570eps|att570eps|AT&T 570 Epson emulation, + + bufsz#10000, + cps#250, + cols#80, + orl#36, + orvi#216, + + use=Gepson+basic, + use=Gepson+low, + +570ibm|att570ibm|AT&T 570 IBM ProPrinter emulation, + + bufsz#10000, + cps#250, + + use=Gibmg+basic, + use=Gibmg+low, + +571eps|att571eps|AT&T 571 Epson emulation, + + cols#136, + + use=570eps, + +571ibm|att571ibm|AT&T 571 IBM ProPrinter emulation, + + cols#136, + use=570ibm, + +572|att572|AT&T 572 9-wire Matrix Printer, + + bufsz#10000, + cps#250, + cols#80, + xhpa@, + xvpa@, + + csnm=%?%p1%{0}%=%tusascii%e%p1%{1}%=%tbritish%e%p1%{2}%=%tfinnish%e%p1%{3}%=%tjapanese%e%p1%{4}%=%tnorwegian%e%p1%{5}%=%tswedish%e%p1%{6}%=%tgerman%e%p1%{7}%=%tfrench%e%p1%{8}%=%tfrench_canadian%e%p1%{9}%=%titalian%e%p1%{10}%=%tspanish%e%p1%{11}%=%tline_drawing%e%p1%{12}%=%tdanish%e%p1%{13}%=%tebcdic%e%p1%{14}%=%tmulti_national%;, + scs=%?%p1%{0}%=%t\E(B%e%p1%{1}%=%t\E(A%e%p1%{2}%=%t\E(C%e%p1%{3}%=%t\E(J%e%p1%{4}%=%t\E(E%e%p1%{5}%=%t\E(H%e%p1%{6}%=%t\E(K%e%p1%{7}%=%t\E(R%e%p1%{8}%=%t\E(Q%e%p1%{9}%=%t\E(Y%e%p1%{10}%=%t\E(Z%e%p1%{11}%=%t\E(O%e%p1%{12}%=%t\E(E%e%p1%{13}%=%t\E(3%e%p1%{14}%=%t\E(<%;, + + is3=\E[0"z, + + snlq=\E[2"z, + snrmq=\E[3"z, + sdrfq=\E[0"z, + sshm=\E[1m, + rshm=\E[22m, + smul=\E[4m, + rmul=\E[24m, + +# The following are not supported by 572/573 + + smgbp@, + smgtp@, + smglp@, + smgrp@, + cud@, + cuf@, + cuu1@, + hpa@, + vpa@, + nel@, + + use=Gdec+basic, + use=Gdec+low, + +573|att573|AT&T 573 9-wire Matrix Printer, + + cols#132, + + use=572, diff --git a/usr/src/cmd/lp/terminfo/58x.ti b/usr/src/cmd/lp/terminfo/58x.ti new file mode 100644 index 0000000000..bd5b65c663 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/58x.ti @@ -0,0 +1,103 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Generic entry: +# + +att583+basic, + + bufsz#16384, + cps#200, + +###################################################################### +# +# Specific printers: +# + +583ibm|att583ibm|AT&T 583 as IBM Proprinter XL, + + cols#136, + is3=\EX\210, + + use=att583+basic, use=Gibmxl+basic, use=Gibmxl+low+5x6, use=Gibmc+color, + + +583ibm-80|att583ibm-80|AT&T 583 as IBM Proprinter XL;80-col, + + use=att583+basic, use=Gibmxl+basic, use=Gibmxl+low+5x6, use=Gibmc+color, + + +583eps|att583eps|AT&T 583 as Epson LQ-2500; low resolution, + + use=att583+basic, use=Gep2500+basic, use=Gep2500+low, use=Gep2500+color, + +583eps-hi|att583eps-hi|AT&T 583 as Epson LQ-2500; high resolution, + + use=att583+basic, use=Gep2500+basic, use=Gep2500+high, use=Gep2500+color, + +583eps-80|att583eps-80|AT&T 583 as Epson LQ-2500; low resolution; 80-col, + + cols#80, + use=att583+basic, use=Gep2500+basic, use=Gep2500+low, use=Gep2500+color, + +583eps-hi-80|att583eps-hi-80|AT&T 583 as Epson LQ-2500; high resolution; 80-col, + + cols#80, + use=att583+basic, use=Gep2500+basic, use=Gep2500+high, use=Gep2500+color, + + +580ibm|att580ibm|AT&T 580 as IBM Proprinter XL, + + use=att583+basic, use=Gibmxl+basic, use=Gibmxl+low+5x6, + +581ibm|att581ibm|AT&T 581 as IBM Proprinter XL, + + cols#136, + is3=\EX\210, + + use=580ibm, + + +581eps|att581eps|AT&T 581 as Epson LQ-2500; low resolution, + + use=att583+basic, use=Gep2500+basic, use=Gep2500+low, + +581eps-hi|att581eps-hi|AT&T 581 as Epson LQ-2500; high resolution, + + use=att583+basic, use=Gep2500+basic, use=Gep2500+high, + +580eps|att580eps|AT&T 580 as Epson LQ-2500; low resolution, + + cols#80, + use=att583+basic, use=Gep2500+basic, use=Gep2500+low, + +580eps-hi|att580eps-hi|AT&T 580 as Epson LQ-2500; high resolution, + + cols#80, + use=att583+basic, use=Gep2500+basic, use=Gep2500+high, diff --git a/usr/src/cmd/lp/terminfo/593.ti b/usr/src/cmd/lp/terminfo/593.ti new file mode 100644 index 0000000000..3d2a592c47 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/593.ti @@ -0,0 +1,73 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Entries for the AT&T 593 printer +# + +593eps|att593eps|AT&T 593 Epson FX86e emulation, + + bufsz#2000, + cps#480, + cols#80, + lines#62, + orl#36, + orvi#216, + +# Only letter quality for laser printer + snlq@, + snrmq@, + sdrfq@, + + use=Gep2500+basic, + use=Gepson+low, + +593ibm|att593ibm|AT&T 593 IBM ProPrinter XL emulation, + + bufsz#2000, + cps#480, + + lines#62, + +# Only letter quality for laser printer + snlq@, + sdrfq@, + + smglp=\EX%p1%{1}%+%c%p2%{1}%+%c, + + use=Gibmxl+basic, + use=Gibmxl+low+1x1, + + +593hp|att593hp|AT&T 593 HP Laserjet II emulation, + + bufsz#2000, + cps#480, + + + use=Ghplaser+II, diff --git a/usr/src/cmd/lp/terminfo/Makefile b/usr/src/cmd/lp/terminfo/Makefile new file mode 100644 index 0000000000..b94f821b6b --- /dev/null +++ b/usr/src/cmd/lp/terminfo/Makefile @@ -0,0 +1,77 @@ +# +# 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. +# +# cmd/lp/terminfo/Makefile +# + +include ../Makefile.lp + +ROOTTERMINFO = $(ROOT)/usr/share/lib/terminfo + +TIC = tic + +SRCS = PS.ti 40.ti 477.ti 53x0.ti 593.ti daisy.ti hplaser.ti \ + 44x.ti 47x.ti 57x.ti dec.ti ibm.ti 45x.ti 495.ti \ + 58x.ti citoh.ti epson.ti unknown.ti + + +TMPSRC = terminfo.src + +DIRMODE= 755 +FILEMODE= 644 + +.KEEP_STATE: + +all : $(TMPSRC) + +$(TMPSRC) : $(SRCS) + $(RM) $@; cat $(SRCS) > $@ + +# +# Since all entries are created at once, we simply choose one of the +# target files and assume everything will be made at one time. This +# has holes (like if somebody removes P/PSR but not P/PS), but those +# are the breaks. +# +install : all $(ROOTTERMINFO) $(ROOTTERMINFO)/P/PS + +$(ROOTTERMINFO)/P/PS: $(TMPSRC) + TERMINFO=$(ROOTTERMINFO) 2>&1 $(TIC) -v $(TMPSRC) > errs + @$(ECHO) "\n`2>/dev/null cat errs|wc -l` entries have been compiled\n" + @-( 2>/dev/null cat errs|grep -iv "^mkdir"|grep -iv "^create"|grep -iv "^link"|grep -vi $(TMPSRC)|grep -vi touch|grep -vi "working"; \ + if [ $$? -ne 0 ] ; \ + then \ + $(ECHO) "\tNo errors\n"; \ + else \ + $(ECHO) "\n\tErrors can be found in `pwd`/errs\n"; \ + fi \ + ) + +$(ROOTTERMINFO) : + $(INS.dir) + +clean clobber: + $(RM) $(TMPSRC) + +strip lint : + diff --git a/usr/src/cmd/lp/terminfo/PS.ti b/usr/src/cmd/lp/terminfo/PS.ti new file mode 100644 index 0000000000..28a83e988b --- /dev/null +++ b/usr/src/cmd/lp/terminfo/PS.ti @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +# ident "%Z%%M% %I% %E% SMI" /* SVr4 1.1 */ + +PS|PSR|PS-b|PS-r|PS-br|Fake PostScript entry, + slines=\004, + u9=\004, + csnm=\004, + scs=\004, + cpi=null, + lpi=null, + cols#80, + lines#66, diff --git a/usr/src/cmd/lp/terminfo/citoh.ti b/usr/src/cmd/lp/terminfo/citoh.ti new file mode 100644 index 0000000000..c2b584b7c6 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/citoh.ti @@ -0,0 +1,127 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ + +###################################################################### +# +# Generic entry for the C.Itoh 8510 printer and emulations: +# + +Gcitoh+basic, + +# The 8510 I use only allows 79 columns! + cols#79, + + lines#66, + it#8, + + orc#1, + orhi#10, + orl#24, + orvi#144, + + cr=\r, + cud1=\n, + cuf1=\s, + ff=\f, + ht=\t, + + cpi=%?%p1%{10}%=%t\EN%e%p1%{12}%=%t\EE%e%p1%{16}%=%p1%{17}%=%O%t\EQ%;, + cvr=%?%p1%{0}%>%p1%{100}%<%&%t\ET%p1%02d%;, + + is1=^Q^X, + is2=\Ec1\Ev66., + + smso=\E!, + rmso=\E", + smul=\EX, + rmul=\EY, + bold=\E!, + ssubm=\Es2, + rsubm=\Es0, + ssupm=\Es1, + rsupm=\Es0, + swidm=^N, + rwidm=^O, + sgr0=\E"\EY\Es0^O, + sgr=%?%p1%p6%|%t\E!%e\E"%;%?%p2%t\EX%e\EY%;, + + rep=\ER%p2%03d%p1%c, + + snlq=\Em2, + snrmq=\Em1, + sdrfq=\Em0, + + smglp=\EL%p1%03d, + smgrp=\E/%{1}%p1%+%03d, + +# slines= + u9=\Ev%p1%02d., + slines=\Ev%p1%02d., + +Gcitoh+low, + + npins#8, + spinv#68, + spinh#136, + + porder=8\,7\,6\,5\,4\,3\,2\,1;0, + + sbim=\ES%p1%04d, + +# birep= + u4=\EV%p2%04d%p3%c, + birep=\EV%p2%04d%p3%c, + + bitwin#1, + u1=1, + bitype#1, + u2=1, + +# defbi= +# Set the line spacing to 17/144 inch to get (almost) 68 dots +# per inch vertically (8 * 144/17). +# Set the character spacing to compressed (1/17 inch or 17 +# characters per inch); at 136 dots per inch horizontally this +# means 8 dots per character. +# Set the left margin at the left edge of the image. +# The C.Itoh doesn't have parameterized vertical motion, +# so we simulate it with linefeeds. Assume we never need +# to move more than 63 lines (at 17/144 LPI). +# Set uni-directional motion; bi-directional causes a wavy +# image. +# defbi= + u6=%?%p5%{1}%=%t\ET17\EQ\EL%p1%{8}%/%03d%p2%{8}%/%Py%?%gy%{31}%>%t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n%gy%{32}%-%Py%;%?%gy%{15}%>%t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n%gy%{16}%-%Py%;%?%gy%{7}%>%t\n\n\n\n\n\n\n\n%gy%{8}%-%Py%;%?%gy%{3}%>%t\n\n\n\n%gy%{4}%-%Py%;%?%gy%{1}%>%t\n\n%gy%{2}%-%Py%;%?%gy%{0}%>%t\n%;\E>%;, + defbi=%?%p5%{1}%=%t\ET17\EQ\EL%p1%{8}%/%03d%p2%{8}%/%Py%?%gy%{31}%>%t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n%gy%{32}%-%Py%;%?%gy%{15}%>%t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n%gy%{16}%-%Py%;%?%gy%{7}%>%t\n\n\n\n\n\n\n\n%gy%{8}%-%Py%;%?%gy%{3}%>%t\n\n\n\n%gy%{4}%-%Py%;%?%gy%{1}%>%t\n\n%gy%{2}%-%Py%;%?%gy%{0}%>%t\n%;\E>%;, + +# endbi= + u7=\EA\EP\EL001\E<, + endbi=\EA\EP\EL001\E<, + +# binel= + u5=\n\r\EL%p1%{8}%/%03d, + binel=\n\r\EL%p1%{8}%/%03d, + diff --git a/usr/src/cmd/lp/terminfo/daisy.ti b/usr/src/cmd/lp/terminfo/daisy.ti new file mode 100644 index 0000000000..26c7b31d9f --- /dev/null +++ b/usr/src/cmd/lp/terminfo/daisy.ti @@ -0,0 +1,126 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Generic entry for the daisy wheel printers and emulations: +# + +# +# Basic capabilities: +# +Gdaisy+basic, + + daisy, + + cols#132, + lines#66, + + bufsz#500, + cps#55, + orc#12, + orhi#120, + orl#8, + orvi#48, + + cr=^M, + cud1=^J, + cuf1=\s, + ff=^L, + ht=^I, + hpa=%?%p1%{100}%<%t\EC%p1%02d%e%p1%{110}%<%t\ECA%p1%{100}%-%d%e%p1%{120}%<%t\ECB%p1%{110}%-%d%e%p1%{130}%<%t\ECC%p1%{120}%-%d%e%p1%{140}%<%t\ECD%p1%{130}%-%d%e%p1%{150}%<%t\ECE%p1%{140}%-%d%e%p1%{160}%<%t\ECF%p1%{150}%-%d%;, + vpa=%?%p1%{100}%<%t\EP%p1%02d%e%p1%{110}%<%t\EPA%p1%{100}%-%d%e%p1%{120}%<%t\EPB%p1%{110}%-%d%e%p1%{130}%<%t\EPC%p1%{120}%-%d%e%p1%{140}%<%t\EPD%p1%{130}%-%d%e%p1%{150}%<%t\EPE%p1%{140}%-%d%e%p1%{160}%<%t\EPF%p1%{150}%-%d%;, + + chr=%?%p1%{100}%<%t\EE%p1%02d%e%p1%{110}%<%t\EEA%p1%{100}%-%d%e%p1%{120}%<%t\EEB%p1%{110}%-%d%e%p1%{130}%<%t\EEC%p1%{120}%-%d%e%p1%{140}%<%t\EED%p1%{130}%-%d%e%p1%{150}%<%t\EEE%p1%{140}%-%d%e%p1%{160}%<%t\EEF%p1%{150}%-%d%;, + cvr=%?%p1%{100}%<%t\EL%p1%02d%e%p1%{110}%<%t\ELA%p1%{100}%-%d%e%p1%{120}%<%t\ELB%p1%{110}%-%d%e%p1%{130}%<%t\ELC%p1%{120}%-%d%e%p1%{140}%<%t\ELD%p1%{130}%-%d%e%p1%{150}%<%t\ELE%p1%{140}%-%d%e%p1%{160}%<%t\ELF%p1%{150}%-%d%;, + + + is2=\E\015P\EW\E.\EL08\EE12\E%\E<, + + smso=\EQ, + rmso=\ER, + smul=\EI, + rmul=\EJ, + bold=\EK3, + sshm=\EQ, + rshm=\ER, + sgr0=\ER\EM\EJ, + sgr=%?%p1%t\EQ%e\ER%;%?%p2%t\EI%e\EJ%;%?%p6%t\EK3%e\EM%;, + + smgb=\E-, + smgl=\E9, + smgr=\E0, + smgt=\E+, + +# slines=, + u9=\EF%p1%02d, + +# +# Graphics capabilities: +# +Gdaisy+lowres, + +# +# We could use the graphics on/graphics off control sequences +# (ESC G/ESC 4) but for these problems: +# +# - graphics mode gets turned off when a \r is received; +# - printing a character doesn't cause motion, which +# means that each ``cell'' must be followed by a space; +# - to get the best aspect ratio, three horizontal dots +# must be sent per ``cell'' (using the ESC 3 graphics mode +# (1/60 instead of 1/120) alleviates this problem but +# gives a worse aspect ratio). +# +# So instead we set the HMI and VMI to 1/40 and 1/48 inch, +# respectively. +# + npins#1, + spinv#48, + spinh#40, + + porder=o\,o\,o\,o\,1\,1\,1\,o;32, + +# bitwin# + u1=1, +# bitype# + u2=1, + +# birep= + u4=%?%p3%{32}%=%t\EH%p2%{3}%*%Px%gx%{256}%/%{64}%+%c%gx%{256}%m%{16}%/%{64}%+%c%gx%{16}%m%{64}%+%c%;, + +# defbi= +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\EL01\EE03%p1%{3}%*%Px\r\EH%gx%{256}%/%{64}%+%c%gx%{256}%m%{16}%/%{64}%+%c%gx%{16}%m%{64}%+%c\EV%p2%{256}%/%{64}%+%c%p2%{256}%m%{16}%/%{64}%+%c%p2%{16}%m%{64}%+%c\E>%;, + +# endbi= + u7=\EL08\EE12\E<, + +# binel= + u5=\n\r%p1%{3}%*%Px\EH%gx%{256}%/%{64}%+%c%gx%{256}%m%{16}%/%{64}%+%c%gx%{16}%m%{64}%+%c, + diff --git a/usr/src/cmd/lp/terminfo/dec.ti b/usr/src/cmd/lp/terminfo/dec.ti new file mode 100644 index 0000000000..5cc0ae6d55 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/dec.ti @@ -0,0 +1,130 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ + +###################################################################### +# +# Generic entries for the various DEC printers and emulations. +# +# The Gdec+... entries are really for any ANSI X3.64 printer, +# but "ansi" is already used for terminals. It would be more +# work to do "use=ansi" because there are too many screen +# oriented caps that would have to be removed. +# + +# +# Basic capabilities: +# +Gdec+basic, + + cols#132, + lines#66, + it#8, + + cpix, + orc#1, + orhi#10, + orl#2, + orvi#12, + +# +# FIX? Are xhpa and xvpa really needed? + xhpa, + xvpa, + + cr=^M, + ff=^L, + ht=^I, + cud1=^J, + cuf1=\s, + cuu1=\EM, + cud=\E[%p1%de, + cuf=\E[%p1%da, + hpa=\E[%p1%d`, + vpa=\E[%p1%dd, + nel=\EE, + + is1=\Ec, + is2=\E[20l, + + cpi=%?%p1%{10}%=%t\E[w%e%p1%{12}%=%t\E[2w%e%p1%{5}%=%t\E[5w%e%p1%{13}%=%p1%{14}%=%O%t\E[3w%e%p1%{16}%=%p1%{17}%=%O%t\E[4w%e%p1%{6}%=%t\E[6w%e%p1%{7}%=%t\E[7w%e%p1%{8}%=%t\E[8w%;, + lpi=%?%p1%{2}%=%t\E[4z%e%p1%{3}%=%t\E[5z%e%p1%{4}%=%t\E[6z%e%p1%{6}%=%t\E[z%e%p1%{8}%=%t\E[2z%e%p1%{12}%=%t\E[3z%;, + + csnm=%?%p1%{0}%=%tusascii%e%p1%{1}%=%tenglish%e%p1%{2}%=%tfinnish%e%p1%{3}%=%tjapanese%e%p1%{4}%=%tnorwegian%e%p1%{5}%=%tswedish%e%p1%{6}%=%tgermanic%e%p1%{7}%=%tfrench%e%p1%{8}%=%tcanadian_french%e%p1%{9}%=%titalian%e%p1%{10}%=%tspanish%e%p1%{11}%=%tline%e%p1%{12}%=%tsecurity%e%p1%{13}%=%tebcdic%e%p1%{14}%=%tapl%e%p1%{15}%=%tmosaic%;, + scs=%?%p1%{0}%=%t\E(B%e%p1%{1}%=%t\E(A%e%p1%{2}%=%t\E(C%e%p1%{3}%=%t\E(D%e%p1%{4}%=%t\E(E%e%p1%{5}%=%t\E(H%e%p1%{6}%=%t\E(K%e%p1%{7}%=%t\E(R%e%p1%{8}%=%t\E(Q%e%p1%{9}%=%t\E(Y%e%p1%{10}%=%t\E(Z%e%p1%{11}%=%t\E(0%e%p1%{12}%=%t\E(1%e%p1%{13}%=%t\E(3%e%p1%{14}%=%t\E(8%e%p1%{15}%=%t\E(}%;, + + sshm=\E[5m, + rshm=\E[m, + + smgtp=\E[%p1%dr, + smgbp=\E[;%p1%dr, + smglp=\E[%{1}%p1%+%ds, + smgrp=\E[;%{1}%p1%+%ds, + +# slines= + u9=\E[%p1%dt, + slines=\E[%p1%dt, + +# +# Graphics capabilities (low resolution, 6-pin): +# +Gdec+low, + + npins#6, + spinv#72, + spinh#75, + + porder=o\,o\,6\,5\,4\,3\,2\,1;63, + + bitwin#1, + u1=1, + bitype#1, + u2=1, + +# birep= + u4=!%p2%d%p3%c, + birep=!%p2%d%p3%c, + +# defbi= +# X is in 1/75 increments; set char spacing to 1/16.7 +# increments to allow us to get close; column is X*16.7/75. +# Y is in 1/72 increments; set line spacing to 1/12 +# increments to allow us to get close; line is Y/6. + u6=%?%p5%{1}%=%t\E[4w\E[%p1%{167}%*%{750}%/%d`\E[w\E[3z\E[%p2%{6}%/%dd\E[z\EP0q%;, + defbi=%?%p5%{1}%=%t\E[4w\E[%p1%{167}%*%{750}%/%d`\E[w\E[3z\E[%p2%{6}%/%dd\E[z\EP0q%;, + +# endbi= + u7=^X, + endbi=^X, + +# binel= + u5=-, + binel=-, + +# bicr= + u3=$, + bicr=$, + diff --git a/usr/src/cmd/lp/terminfo/epson.ti b/usr/src/cmd/lp/terminfo/epson.ti new file mode 100644 index 0000000000..8beceb1e74 --- /dev/null +++ b/usr/src/cmd/lp/terminfo/epson.ti @@ -0,0 +1,301 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Epson +# + +# +# Basic capabilities: +# +Gepson+basic, + + cols#80, + lines#66, + it#8, + + cpix, + orc#6, + orhi#60, + orl#30, + orvi#180, + + cr=^M, + cud1=^J, + cuf1=\s, + cub1=\b, + ff=^L, + ht=^I, + + cpi=%?%p1%{10}%=%t^R\EP%e%p1%{12}%=%t^R\EM%e%p1%{20}%=%t^O\EM%e%p1%{17}%=%t^O\EP%;, + cvr=%?%p1%{0}%>%p1%{256}%<%&%t\E3%p1%c%;, + + is1=^Q^X, + is2=\E@\E%0\EO, + + csnm=%?%p1%{0}%=%tusa%e%p1%{1}%=%tfrench%e%p1%{2}%=%tgerman%e%p1%{3}%=%tbritish%e%p1%{4}%=%tdanish%e%p1%{5}%=%tswedish%e%p1%{6}%=%titalian%e%p1%{7}%=%tspanish%e%p1%{8}%=%tjapanese%e%p1%{9}%=%tnorwegian%e%p1%{10}%=%tdanish2%e%p1%{11}%=%tspanish2%e%p1%{12}%=%tlatin_american%e%p1%{13}%=%tafrikaans%e%p1%{14}%=%tdutch%e%p1%{15}%=%tfrench_canadian%e%p1%{16}%=%tfrench2%e%p1%{17}%=%tbritish2%e%p1%{18}%=%tmulti_national%e%p1%{19}%=%tibmgraphics%;, + scs=%?%p1%{0}%=%t\ER\200%e%p1%{1}%=%t\ER\001%e%p1%{2}%=%t\ER\002%e%p1%{3}%=%t\ER\003%e%p1%{4}%=%t\ER\004%e%p1%{5}%=%t\ER\005%e%p1%{6}%=%t\ER\006%e%p1%{7}%=%t\ER\007%e%p1%{8}%=%t\ER\010%e%p1%{9}%=%t\ER\011%e%p1%{10}%=%t\ER\012%e%p1%{11}%=%t\ER\013%e%p1%{12}%=%t\ER\014%e%p1%{13}%=%t\ER\100%e%p1%{14}%=%t\ERA%e%p1%{15}%=%t\ERB%e%p1%{16}%=%t\ERC%e%p1%{17}%=%t\ERD%e%p1%{18}%=%t\E6%e%p1%{19}%=%t\Et1%;, + + smso=\EE, + rmso=\EF, + smul=\E-1, + rmul=\E-0, + bold=\EG, + sshm=\EE, + rshm=\EF, + ssubm=\ES1, + rsubm=\ET, + ssupm=\ES0, + rsupm=\ET, + swidm=\EW1, + rwidm=\EW0, + sitm=\E4, + ritm=\E5, + sgr0=\EF\E-0\EH\ET\EW0\E5, + sgr=%?%p1%t\EE%e\EF%;%?%p2%t\E-1%e\E-0%;%?%p6%t\EG%e\EH%;, + +# +# For now we can't set the margin in the first (0th) column +# due to limitations in the Curses code. This should be changed +# in the future. For now, shift right 1. Note that the right +# margin is the last USEABLE column in Terminfo, but is 1 +# PAST that for the Epson. + smglp=%?%p1%{256}%<%t\El%p1%{1}%+%c%;, + smgrp=%?%p1%{256}%<%t\EQ%p1%{2}%+%c%;, + +# slines= u9 used for svr3.2 + u9=%?%p1%{0}%>%p1%{128}%<%&%t\EC%p1%c%;, + slines=%?%p1%{0}%>%p1%{128}%<%&%t\EC%p1%d%;, + + sdrfq=\Ex0, + snlq=\Ex1, + snrmq=\Ek1, + +# +# Graphics capabilities: +# +Gepson+low, + + npins#8, + spinv#60, + spinh#60, + + porder=1\,2\,3\,4\,5\,6\,7\,8;0, + + sbim=\EK%p1%{256}%m%c%p1%{256}%/%c, + +# u1 - u8 used for svr3.2 + bitwin#1, + u1=1, + bitype#1, + u2=1, + +# defbi= +# Set the line spacing to 8/60 inch (7.5 lines per inch) +# to get 60 dots per inch vertically (7.5 lines/" * 8 pins/line). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 60 dots per inch horizontally this means 6 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + defbi=%?%p5%{1}%=%t\E3\030^R\EP\EB%p2%{8}%/%c$<>\ED%p1%{6}%/%c$<>\013\r\t%;, + +# endbi= + u7=\E3\036, + endbi=\E3\036, + +# binel= + u5=\n\r\t, + binel=\n\r\t, + +# bicr= + u3=\r\t, + bicr=\r\t, + +###################################################################### +# +# Epson LQ-2500 +# + +# +# Basic capabilities: +# +Gep2500+basic, + + cols#136, + lines#66, + it#8, + + cpix, + orc#6, + orhi#60, + orl#30, + orvi#180, + + cr=^M, + cud1=^J, + cuf1=\s, + cub1=\b, + ff=^L, + ht=^I, + + cpi=%?%p1%{10}%=%t^R\EP%e%p1%{12}%=%t^R\EM%e%p1%{20}%=%t^O\EM%e%p1%{17}%=%t^O\EP%;, + cvr=%?%p1%{0}%>%p1%{256}%<%&%t\E3%p1%c%;, + + is1=^Q^X, + is2=\E@\E%0\EO, + + + csnm=%?%p1%{0}%=%tusa%e%p1%{1}%=%tfrench%e%p1%{2}%=%tgerman%e%p1%{3}%=%tbritish%e%p1%{4}%=%tdanish%e%p1%{5}%=%tswedish%e%p1%{6}%=%titalian%e%p1%{7}%=%tspanish%e%p1%{8}%=%tjapanese%e%p1%{9}%=%tnorwegian%e%p1%{10}%=%tdanish2%e%p1%{11}%=%tspanish2%e%p1%{12}%=%tlatin_american%e%p1%{13}%=%tibmgraphics%;, + scs=%?%p1%{0}%=%t\ER\200%e%p1%{1}%=%t\ER\001%e%p1%{2}%=%t\ER\002%e%p1%{3}%=%t\ER\003%e%p1%{4}%=%t\ER\004%e%p1%{5}%=%t\ER\005%e%p1%{6}%=%t\ER\006%e%p1%{7}%=%t\ER\007%e%p1%{8}%=%t\ER\010%e%p1%{9}%=%t\ER\011%e%p1%{10}%=%t\ER\012%e%p1%{11}%=%t\ER\013%e%p1%{12}%=%t\ER\014%e%p1%{13}%=%t\Et1%;, + + smso=\EE, + rmso=\EF, + smul=\E-1, + rmul=\E-0, + bold=\EG, + sshm=\EE, + rshm=\EF, + ssubm=\ES1, + rsubm=\ET, + ssupm=\ES0, + rsupm=\ET, + swidm=\EW1, + rwidm=\EW0, + sitm=\E4, + ritm=\E5, + sgr0=\EF\E-0\EH\ET\EW0\E5, + sgr=%?%p1%t\EE%e\EF%;%?%p2%t\E-1%e\E-0%;%?%p6%t\EG%e\EH%;, + +# +# For now we can't set the margin in the first (0th) column +# due to limitations in the Curses code. This should be changed +# in the future. For now, shift right 1. Note that the right +# margin is the last USEABLE column in Terminfo, but is 1 +# PAST that for the Epson. + smglp=%?%p1%{256}%<%t\El%p1%{1}%+%c%;, + smgrp=%?%p1%{256}%<%t\EQ%p1%{2}%+%c%;, + +# slines= + u9=%?%p1%{0}%>%p1%{128}%<%&%t\EC%p1%c%;, + slines=%?%p1%{0}%>%p1%{128}%<%&%t\EC%p1%c%;, + + sdrfq=\Ex0, + snlq=\Ex1, + snrmq=\Ek1, + +# +# Graphics capabilities: +# +Gep2500+low, + + npins#8, + spinv#60, + spinh#60, + + porder=1\,2\,3\,4\,5\,6\,7\,8;0, + + sbim=\EK%p1%{256}%m%c%p1%{256}%/%c, + + bitwin#1, + u1=1, + bitype#1, + u2=1, + +# defbi= +# Set the line spacing to 8/60 inch (7.5 lines per inch) +# to get 60 dots per inch vertically (7.5 lines/" * 8 pins/line). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 60 dots per inch horizontally this means 6 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\E3\030^R\EP\EB%p2%{8}%/%c$<>\ED%p1%{6}%/%c$<>\013\r\t%;, + defbi=%?%p5%{1}%=%t\E3\030^R\EP\EB%p2%{8}%/%c$<>\ED%p1%{6}%/%c$<>\013\r\t%;, + +# endbi= + u7=\E3\036, + endbi=\E3\036, + +# binel= + u5=\n\r\t, + binel=\n\r\t, + +# bicr= + u3=\r\t, + bicr=\r\t, + +# +# Graphics capabilities: +# +Gep2500+high, + + npins#24, + spinv#180, + spinh#180, + + porder=1\,2\,3\,4\,5\,6\,7\,8\,9\,10\,11\,12\,13\,14\,15\,16\,17\,18\,19\,20\,21\,22\,23\,24;0, + + sbim=\E*\047%p1%{256}%m%c%p1%{256}%/%c, + +# defbi= +# Set the line spacing to 8/60 inch (7.5 lines per inch) +# to get 180 dots per inch vertically (7.5 lines/" * 24 pins/line). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 180 dots per inch horizontally this means 18 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\E3\030^R\EP\EB%p2%{24}%/%c$<>\ED%p1%{18}%/%c$<>\013\r\t%;, + defbi=%?%p5%{1}%=%t\E3\030^R\EP\EB%p2%{24}%/%c$<>\ED%p1%{18}%/%c$<>\013\r\t%;, + + use=Gep2500+low, + +# +# Color capability: +# +Gep2500+color, + + colors#3, + +# setcolor= + initc=%?%p1%{0}%=%t\Er0%;%?%p1%{1}%=%t\Er2%;%?%p1%{2}%=%t\Er1%;%?%p1%{3}%=%t\Er4%;, + +# colornm= + u8=%?%p1%{0}%=%tblack%;%?%p1%{1}%=%tcyan%;%?%p1%{2}%=%tmagenta%;%?%p1%{3}%=%tyellow%;%?%p1%{4}%=%torange=yellow+magenta%;%?%p1%{5}%=%tgreen=yellow+cyan%;%?%p1%{6}%=%tviolet=magenta+cyan%;%?%p1%{7}%=%tbrown=magenta+black%;, + colornm=%?%p1%{0}%=%tblack%;%?%p1%{1}%=%tcyan%;%?%p1%{2}%=%tmagenta%;%?%p1%{3}%=%tyellow%;%?%p1%{4}%=%torange=yellow+magenta%;%?%p1%{5}%=%tgreen=yellow+cyan%;%?%p1%{6}%=%tviolet=magenta+cyan%;%?%p1%{7}%=%tbrown=magenta+black%;, + diff --git a/usr/src/cmd/lp/terminfo/hplaser.ti b/usr/src/cmd/lp/terminfo/hplaser.ti new file mode 100644 index 0000000000..4d887f99ab --- /dev/null +++ b/usr/src/cmd/lp/terminfo/hplaser.ti @@ -0,0 +1,122 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ + +###################################################################### +# +# Generic entry for the HP Laserjet printers and emulations: +# + +# +# Basic capabilities: +# +Ghplaser+basic, + + cols#80, + lines#60, + + orc#12, + orhi#120, + orl#8, + orvi#48, + + cr=^M, + cud1=^J, + cuf1=\s, + cud=\E&a+%p1%dR, + cuf=\E&a+%p1%dC, + ff=^L, + hpa=\E&a%p1%dC, + vpa=\E&a%p1%dR, + + cpi=%?%p1%{10}%=%t\E&k0S%e%p1%{17}%=%t\E&k2S%;, + chr=%?%p1%{0}%>%p1%{127}%<%t\E&k%p1%dH%;, + cvr=%?%p1%{0}%>%p1%{127}%<%t\E&l%p1%dC%;, + lpi=%?%p1%{1}%=%t\E&l1D%e%p1%{2}%=%t\E&l2D%e%p1%{3}%=%t\E&l3D%e%p1%{4}%=%t\E&l4D%e%p1%{6}%=%t\E&l6D%e%p1%{8}%=%t\E&l8D%e%p1%{12}%=%t\E&l12D%e%p1%{16}%=%t\E&l16D%e%p1%{24}%=%t\E&l24D%e%p1%{48}%=%t\E&l48D%;, + + is2=\EE\E&k0G, + mgc=\E9, + + rmul=\E&d\100, + ritm=\E(s0S, + smul=\E&dD, + sitm=\E(s1S, + smgtp=\E&l%p1%{1}%+%dE, + smgbp=\E&l%p1%{1}%+%dF, + smglp=\E&a%p1%dL, + smgrp=\E&a%p1%dM, + +#Set top margin at +2 offset + smgtp=\E&l%p1%dE, + +#Set page length u9 used for 3.2 slines for 4.0 + u9=\E&l%p1P, + slines=\E&l%p1%dF, + +# +# Graphics capabilities: +# +Ghplaser+high, + + npins#8, + spinv#300, + spinh#300, + + porder=1\,2\,3\,4\,5\,6\,7\,8;0, + + sbim=\E*b%p1%dW, + +# u1 - u7 used for svr3.2 +# bitwin#, + u1=1, + bitwin#1, +# bitype# + u2=2, + bitype#2, + +# defbi= +# X (or Y) * scale * 12/5 == pos in decipoints (12/5 == 720/300) + u6=%?%p5%{0}%>%p5%{5}%<%&%t\E&a%p1%p5%*%{12}%*%{5}%/%dH\E&a%p2%p5%*%{12}%*%{5}%/%dV\E*t%{300}%p5%/%dR\E*r1A%;, + defbi=%?%p5%{0}%>%p5%{5}%<%&%t\E&a%p1%p5%*%{12}%*%{5}%/%dH\E&a%p2%p5%*%{12}%*%{5}%/%dV\E*t%{300}%p5%/%dR\E*r1A%;, + +# endbi= + u7=\E*rB, + endbi=\E*rB, + +Ghplaser+II, + + cpi=%?%p1%{10}%=%t\E(s10H%e%p1%{16}%=%p1%{17}%=%O%t\E(s16.66H%e%;, + + csnm=%?%p1%{0}%=%troman-8%e%p1%{1}%=%tibm-us%e%p1%{2}%=%tibm-dn%e%p1%{3}%=%tgerman%e%p1%{4}%=%tspanish%e%p1%{5}%=%tecma-94%e%p1%{6}%=%tiso2%e%p1%{7}%=%tiso4%e%p1%{8}%=%tiso6%e%p1%{9}%=%tiso10%e%p1%{10}%=%tiso11%e%p1%{11}%=%tiso14%e%p1%{12}%=%tiso15%e%p1%{13}%=%tiso16%e%p1%{14}%=%tiso17%e%p1%{15}%=%tiso21%e%p1%{16}%=%tiso25%e%p1%{17}%=%tiso57%e%p1%{18}%=%tiso60%e%p1%{19}%=%tiso61%e%p1%{20}%=%tiso69%e%p1%{21}%=%tiso84%e%p1%{22}%=%tiso85%;, + + scs=%?%p1%{0}%=%t\E(8U%e%p1%{1}%=%t\E(10U%e%p1%{2}%=%t\E(11U%e%p1%{3}%=%t\E(0G%e%p1%{4}%=%t\E(1S%e%p1%{5}%=%t\E(0N%e%p1%{6}%=%t\E(2U%e%p1%{7}%=%t\E(1E%e%p1%{8}%=%t\E(0U%e%p1%{9}%=%t\E(3S%e%p1%{10}%=%t\E(0S%e%p1%{11}%=%t\E(0K%e%p1%{12}%=%t\E(0I%e%p1%{13}%=%t\E(4S%e%p1%{14}%=%t\E(2S%e%p1%{15}%=%t\E(1G%e%p1%{16}%=%t\E(0F%e%p1%{17}%=%t\E(2K%e%p1%{18}%=%t\E(0D%e%p1%{19}%=%t\E(1D%e%p1%{20}%=%t\E(1F%e%p1%{21}%=%t\E(5S%e%p1%{22}%=%t\E(6S%;, + + use=Ghplaser+basic, use=Ghplaser+high, + diff --git a/usr/src/cmd/lp/terminfo/ibm.ti b/usr/src/cmd/lp/terminfo/ibm.ti new file mode 100644 index 0000000000..e58aca21eb --- /dev/null +++ b/usr/src/cmd/lp/terminfo/ibm.ti @@ -0,0 +1,353 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ + +###################################################################### +# +# Generic entries for the various IBM printers and emulations. +# + +################################### +# +# IBM Graphics +# + +# +# Basic capabilities: +# +Gibmg+basic, + + cols#80, + lines#66, + it#8, + + cpix, + orc#1, + orhi#10, + orl#12, + orvi#72, + + cr=^M, + ff=^L, + ht=^I, + cud1=^J, + cuf1=\s, + + cpi=%?%p1%{10}%=%t^R%e%p1%{16}%=%p1%{17}%=%O%t^O%;, + cvr=%?%p1%{0}%>%p1%{256}%<%&%t\EA%p1%c\E2%;, + + is1=^X, + is2=^R\EA\014\E2\EF\EH\EW0\ET\E-0\E7\EO\ECB, + + csnm=%?%p1%{0}%=%tcharacter_set_1%e%p1%{1}%=%tcharacter_set_2%;, + scs=%?%p1%{0}%=%t\E7%e%p1%{2}%=%t\E6%;, + + smso=\EE, + rmso=\EF, + smul=\E-1, + rmul=\E-0, + bold=\EG, + smacs=\E6, + rmacs=\E7, + sshm=\EE, + rshm=\EF, + ssubm=\ES1, + rsubm=\ET, + ssupm=\ES0, + rsupm=\ET, + swidm=\EW1, + rwidm=\EW0, + sgr0=\EF\E-0\EH\E7\ET\EW0, + sgr=%?%p1%t\EE%e\EF%;%?%p2%t\E-1%e\E-0%;%?%p6%t\EG%e\EH%;%?%p9%t\E6%e\E7%;, + sdrfq=\EH, + snlq=\EG, + +# slines= u9 used for svr3.2 + u9=%?%p1%{0}%>%p1%{128}%<%&%t\EC%p1%c%;, + slines=%?%p1%{0}%>%p1%{128}%<%&%t\EC%p1%c%;, + +# +# Graphics capabilities (low resolution, 9-pin): +# +Gibmg+low, + + npins#8, + spinv#72, + spinh#60, + + porder=1\,2\,3\,4\,5\,6\,7\,8;0, + + sbim=\EK%p1%{256}%m%c%p1%{256}%/%c, + + bitwin#1, + u1=1, + bitype#1, + u2=1, + +# defbi= +# Set the line spacing to 8/72 inch (9 lines per inch) +# to get 72 dots per inch vertically (9 lines/inch * 8 pins/line). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 60 dots per inch horizontally this means 6 +# dots per character. +# The IBM Graphics doesn't have parameterized motion, +# so we simulate it with linefeeds and spaces. +# Assume we never need to move across more than 63 colums +# or down more than 31 lines. +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\EA\010\E2^R%p2%{8}%/%Py%?%gy%{15}%>%t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n%gy%{16}%-%Py%;%?%gy%{7}%>%t\n\n\n\n\n\n\n\n%gy%{8}%-%Py%;%?%gy%{3}%>%t\n\n\n\n%gy%{4}%-%Py%;%?%gy%{1}%>%t\n\n%gy%{2}%-%Py%;%?%gy%{0}%>%t\n%;\r%p1%{6}%/%Px%?%gx%{31}%>%t %gx%{32}%-%Px%;%?%gx%{15}%>%t %gx%{16}%-%Px%;%?%gx%{7}%>%t %gx%{8}%-%Px%;%?%gx%{3}%>%t %gx%{4}%-%Px%;%?%gx%{1}%>%t %gx%{2}%-%Px%;%?%gx%{0}%>%t %;%;, + defbi=%?%p5%{1}%=%t\EA\010\E2^R%p2%{8}%/%Py%?%gy%{15}%>%t\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n%gy%{16}%-%Py%;%?%gy%{7}%>%t\n\n\n\n\n\n\n\n%gy%{8}%-%Py%;%?%gy%{3}%>%t\n\n\n\n%gy%{4}%-%Py%;%?%gy%{1}%>%t\n\n%gy%{2}%-%Py%;%?%gy%{0}%>%t\n%;\r%p1%{6}%/%Px%?%gx%{31}%>%t %gx%{32}%-%Px%;%?%gx%{15}%>%t %gx%{16}%-%Px%;%?%gx%{7}%>%t %gx%{8}%-%Px%;%?%gx%{3}%>%t %gx%{4}%-%Px%;%?%gx%{1}%>%t %gx%{2}%-%Px%;%?%gx%{0}%>%t %;%;, + +# endbi= + u7=\EA\014\E2, + endbi=\EA\014\E2, + +# binel= + u5=\n\r%p1%{6}%/%Px%?%gx%{31}%>%t %gx%{32}%-%Px%;%?%gx%{15}%>%t %gx%{16}%-%Px%;%?%gx%{7}%>%t %gx%{8}%-%Px%;%?%gx%{3}%>%t %gx%{4}%-%Px%;%?%gx%{1}%>%t %gx%{2}%-%Px%;%?%gx%{0}%>%t %;, + binel=\n\r%p1%{6}%/%Px%?%gx%{31}%>%t %gx%{32}%-%Px%;%?%gx%{15}%>%t %gx%{16}%-%Px%;%?%gx%{7}%>%t %gx%{8}%-%Px%;%?%gx%{3}%>%t %gx%{4}%-%Px%;%?%gx%{1}%>%t %gx%{2}%-%Px%;%?%gx%{0}%>%t %;, + +# bicr= + u3=\r%p1%{6}%/%Px%?%gx%{31}%>%t %gx%{32}%-%Px%;%?%gx%{15}%>%t %gx%{16}%-%Px%;%?%gx%{7}%>%t %gx%{8}%-%Px%;%?%gx%{3}%>%t %gx%{4}%-%Px%;%?%gx%{1}%>%t %gx%{2}%-%Px%;%?%gx%{0}%>%t %;, + bicr=\r%p1%{6}%/%Px%?%gx%{31}%>%t %gx%{32}%-%Px%;%?%gx%{15}%>%t %gx%{16}%-%Px%;%?%gx%{7}%>%t %gx%{8}%-%Px%;%?%gx%{3}%>%t %gx%{4}%-%Px%;%?%gx%{1}%>%t %gx%{2}%-%Px%;%?%gx%{0}%>%t %;, + +################################### +# +# IBM Color +# + +# +# Basic capabilities: +# +Gibmc+basic, + + cub1=\b, + + is1=^Q^X, + is2=^R\EA\014\E2\EF\EH\EW0\ET\E-0\E7\EO\ER\E50\EM0\EX^A\210\Eb\ECB, + + cvr=%?%p1%{0}%>%p1%{256}%<%&%t\E3%p1%c%;, + + smglp=\EX%p1%{1}%+%c%p2%{1}%+%c, + + use=Gibmg+basic, + +# +# Graphics capabilities (low resolution, 9-pin, 5:6 aspect ratio): +# +Gibmc+low+5x6, + + spinv#84, + spinh#70, + +# defbi= +# Set 5:6 aspect ratio. +# Set the line spacing to 7/72 inch (10.29 lines per inch) +# to get approximately 84 dots per inch vertically +# (10.29 lines/inch * 8 pins/line equals 82.28 dots per inch). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 70 dots per inch horizontally this means 7 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\En^B\E1^R\EB%p2%{8}%/%c$<>\ED%p1%{7}%/%c$<>\013\r\t%;, + defbi=%?%p5%{1}%=%t\En^B\E1^R\EB%p2%{8}%/%c$<>\ED%p1%{7}%/%c$<>\013\r\t%;, + +# binel= + u5=\n\r\t, + binel=\n\r\t, + +# bicr= + u3=\r\t, + bicr=\r\t, + + use=Gibmg+low, + +# +# Graphics capabilities (low resolution, 9-pin, 1:1 aspect ratio): +# +Gibmc+low+1x1, + + spinh#84, + +# defbi= +# Set 1:1 aspect ratio. +# Set the line spacing to 7/72 inch (10.29 lines per inch) +# to get approximately 84 dots per inch vertically +# (10.29 lines/inch * 8 pins/line equals 82.28 dots per inch). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 84 dots per inch horizontally this means 8.4 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\En^A\E1^R\EB%p2%{8}%/%c$<>\ED%p1%{10}%*%{84}%/%c$<>\013\r\t%;, + defbi=%?%p5%{1}%=%t\En^A\E1^R\EB%p2%{8}%/%c$<>\ED%p1%{10}%*%{84}%/%c$<>\013\r\t%;, + + use=Gibmc+low+5x6, + +# +# Color capability: +# +Gibmc+color, + + colors#3, + +# setcolor= + initc=%?%p1%{0}%=%t\Eb%;%?%p1%{1}%=%t\Ec%;%?%p1%{2}%=%t\Em%;%?%p1%{3}%=%t\Ey%;, + +# colornm= + u8=%?%p1%{0}%=%tblack%;%?%p1%{1}%=%tcyan%;%?%p1%{2}%=%tmagenta%;%?%p1%{3}%=%tyellow%;%?%p1%{4}%=%torange=yellow+magenta%;%?%p1%{5}%=%tgreen=yellow+cyan%;%?%p1%{6}%=%tviolet=magenta+cyan%;%?%p1%{7}%=%tbrown=magenta+black%;, + colornm=%?%p1%{0}%=%tblack%;%?%p1%{1}%=%tcyan%;%?%p1%{2}%=%tmagenta%;%?%p1%{3}%=%tyellow%;%?%p1%{4}%=%torange=yellow+magenta%;%?%p1%{5}%=%tgreen=yellow+cyan%;%?%p1%{6}%=%tviolet=magenta+cyan%;%?%p1%{7}%=%tbrown=magenta+black%;, + +################################### +# +# IBM Proprinter XL: +# +# This printer appears to be a superset of the IBM Graphics +# and IBM Color printers, with a 24-wire printhead. The entry +# below uses the full capabilities of the superset and printhead. +# The printer has an Alternate Graphics Mode (AGM) that changes +# the vertical resolution from 1/216" to 1/180", and the graphics +# aspect ratio from 5:6 to 1:1. HOWEVER, there does not appear to +# be a control sequence that switches into this mode--it must be +# done by hand! +# + +# +# Basic capabilities (printer not in AGM): +# +Gibmxl+basic, + + orc#12, + orhi#120, + orl#36, + orvi#216, + + cub1=\b, + + cpi=%?%p1%{10}%=%t^R%e%p1%{12}%=%t\E:%e%p1%{17}%=%t^O%;, + cvr=%?%p1%{0}%>%p1%{256}%<%&%t\E3%p1%c%;, + + is1=^Q^X, + is2=^R\EP0\EA\014\E2\EC\102\EO\ER\Eb\E50\EF\EH\EW0\ET\E-0\E_0\E7, + is3=\EX\001\120, + + smglp=\EX%p1%{1}%+%c%p2%{1}%+%c, + + use=Gibmg+basic, + +# +# Basic capabilities (printer in AGM): +# +Gibmxlagm+basic, + + orl#30, + orvi#180, + + is2=^R\EP0\EA\012\E2\EC\102\EO\ER\Eb\E50\EF\EH\EW0\ET\E-0\E_0\E7, + + use=Gibmxl+basic, + +# +# Graphics capabilities (low resolution, 8-pin, 5:6 aspect ratio): +# +Gibmxl+low+5x6, + + spinv#72, + spinh#60, + +# defbi= +# Set the line spacing to 8/72 inch (9 lines per inch) +# to get 72 dots per inch vertically (9 lines/" * 8 pins/line). +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 60 dots per inch horizontally this means 6 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\EA\010\E2^R\EB%p2%{8}%/%c$<>\ED%p1%{6}%/%c$<>\013\r\t%;, + defbi=%?%p5%{1}%=%t\EA\010\E2^R\EB%p2%{8}%/%c$<>\ED%p1%{6}%/%c$<>\013\r\t%;, + + use=Gibmc+low+5x6, + +# +# Graphics capabilities (low resolution, 8-pin, 1:1 aspect ratio): +# +Gibmxl+low+1x1, + + spinv#60, + +# In AGM the "defbi" cap from Gibmxl+low+5x6 will work. The +# line spacing will be 8/60 inch to get 60 dots per inch, using +# the same control sequence. + + use=Gibmxl+low+5x6, + +# +# Graphics capabilities (high resolution, 24-pin, 5:6 aspect ratio): +# +# This doesn't work as the pin spacing doesn't get set to +# 1/216 inch, but stays at 1/180 inch, even out of AGM. +# + +# +# Graphics capabilities (high resolution, 24-pin, 1:1 aspect ratio): +# +Gibmxl+high+1x1, + + npins#24, + spinv#180, + spinh#180, + + porder=1\,2\,3\,4\,5\,6\,7\,8\,9\,10\,11\,12\,13\,14\,15\,16\,17\,18\,19\,20\,21\,22\,23\,24;0, + + sbim=\E*\047%p1%{256}%m%c%p1%{256}%/%c, + +# defbi= +# Set the line spacing to 8/60 inch (7.5 lines per inch) +# to get 180 dots per inch vertically (7.5 lines/" * 24 pins/line). +# This requires the printer or emulation in Alternate Graphics Mode. +# Set the character spacing to pica (1/10 inch or 10 characters +# per inch); at 180 dots per inch horizontally this means 18 +# dots per character. +# Set vertical and horizontal tab stops at the upper left corner +# of the image, then tab to the upper left corner. +# Note: $<> is a true null (only works with special Curses routine). +# THIS ASSUMES WE START AT THE TOP OF THE PAGE! (although +# maybe not in the first column.) + u6=%?%p5%{1}%=%t\EA\010\E2^R\EB%p2%{24}%/%c$<>\ED%p1%{18}%/%c$<>\013\r\t%;, + defbi=%?%p5%{1}%=%t\EA\010\E2^R\EB%p2%{24}%/%c$<>\ED%p1%{18}%/%c$<>\013\r\t%;, + + use=Gibmc+low+5x6, + diff --git a/usr/src/cmd/lp/terminfo/unknown.ti b/usr/src/cmd/lp/terminfo/unknown.ti new file mode 100644 index 0000000000..85f95a606e --- /dev/null +++ b/usr/src/cmd/lp/terminfo/unknown.ti @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T +# All Rights Reserved + + +#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ +unknown, + am, gn, + cols#80, + bel=^G, cr=\r, cud1=\n, ind=\n, diff --git a/usr/src/cmd/machid/Makefile b/usr/src/cmd/machid/Makefile new file mode 100644 index 0000000000..6a3c7138dc --- /dev/null +++ b/usr/src/cmd/machid/Makefile @@ -0,0 +1,80 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +PROG= machid + +include ../Makefile.cmd + +# +# List of all links present on all architectures and machines. +# +# Note that this function is obsolesent and we don't generally +# add to this list (see psarc/1992/171). +# +FIRSTLINK = i286 +LINKS = i386 i486 i860 i86pc iAPX286 \ + m68k mc68000 mc68010 mc68020 mc68030 mc68040 \ + sparc sun sun2 sun3 sun3x sun4 sun4c sun4m sun4d sun4e \ + u370 u3b u3b15 u3b2 u3b5 vax pdp11 + +ROOTFIRSTLINK = $(ROOTBIN)/$(FIRSTLINK) +ROOTLINKS = $(LINKS:%=$(ROOTBIN)/%) + +# +# Install the program as the first machine in the list. +# +INSTALLIT= $(INS.link) +$(ROOTFIRSTLINK):= INSTALLIT = $(INS.rename) +$(ROOTLINKS):= INSLINKTARGET = $(ROOTFIRSTLINK) + +$(ROOTLINKS): $(ROOTFIRSTLINK) + +# +# Link installation rules +# +$(ROOTBIN)/%: $(PROG) + $(INSTALLIT) + +$(ROOTFIRSTLINK): $(ROOTBIN) + +$(ROOTBIN): + $(INS.dir) + +CFLAGS += $(CCVERBOSE) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTLINKS) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/machid/machid.c b/usr/src/cmd/machid/machid.c new file mode 100644 index 0000000000..c3d57b91f9 --- /dev/null +++ b/usr/src/cmd/machid/machid.c @@ -0,0 +1,137 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1993-1994, by Sun Microsystems, Inc. + */ + +/* + * This program replicates the function of the links from a machine name + * (such as sun4c) through /usr/kvm to true or false as appropriate. It + * knows the correct special cases. + * + * IMPORTANT NOTE: + * + * Do not modify this program to know about additional special cases or + * reflect new platforms or instruction set architectures. This is a + * deprecated interface and strictly for backwards compatibility. This + * is psarc/1992/171. Note the following excerpt from the opinion: + * + * It is most important to note that the manual page states in + * the NOTES section: "The machid family of commands is + * obsolete. Use uname -p and uname -m instead." + * + * The intent of Kernel Architecture Project team is to provide + * only enough functionality to mimic the existing definitions + * on the SPARC and Intel x86 versions of Solaris 2.x. No new + * identifiers will ever be added to the documented and + * undocumented identifiers listed above. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <sys/systeminfo.h> + +static char static_buf[SYS_NMLN]; +static char *progname; + +static void get_info_item(int command, char **buf, long *count); + +/* ARGSUSED */ +int +main(int argc, char *argv[], char *envp[]) +{ + char *buf = &static_buf[0]; + long buflen = SYS_NMLN; + + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + progname++; + + /* + * First possible match is on the processor type. + * + * Special case for architectures: i386 matches i486 and visa versa. + */ + get_info_item(SI_ARCHITECTURE, &buf, &buflen); + if (strcmp(buf, progname) == 0) + return (0); + if ((strcmp(buf, "i386") == 0 && strcmp(progname, "i486") == 0) || + (strcmp(buf, "i486") == 0 && strcmp(progname, "i386") == 0)) + return (0); + + /* + * Next possible match is the machine, or more exactly, the value + * which would be returned by uname(2) in the machine field or uname(1) + * with the -m option. For historical reasons this is really is + * often a class of platforms which are identical to userland processes + * such as sun4c, sun4m, etc. + */ + get_info_item(SI_MACHINE, &buf, &buflen); + if (strcmp(buf, progname) == 0) + return (0); + + /* + * Finally, match the vendor. We hardwire in one historical match. + */ + get_info_item(SI_HW_PROVIDER, &buf, &buflen); + if (strcmp(buf, progname) == 0) + return (0); + if (strcasecmp(buf, "Sun_Microsystems") == 0 && + strcmp("sun", progname) == 0) + return (0); + + return (255); +} + +/* + * get_info_item is a wrapper around the sysinfo system call. It makes sure + * the buffer is large enough, returning a larger buffer if needed. On + * unrecoverable error, it exits. An error message doesn't help and makes + * this tiny program link stdio and maybe deal with internationalization, + * so the best thing is to die silently. Note that the larger buffer is + * retained for later use. Reality is that the buffer will always be big + * enough, but this is coded to the spec rather than implementation. + */ +static void +get_info_item(int command, char **buf, long *count) +{ + long error; + + error = sysinfo(command, *buf, *count); + if (error > *count) { + *count = error; + if (*buf != static_buf) { + free(*buf); + } + *buf = (char *) malloc(*count); + error = sysinfo(command, *buf, *count); + } + + if (error == -1) + exit(-1); +} diff --git a/usr/src/cmd/man/Makefile b/usr/src/cmd/man/Makefile index 911bae6340..7c7afebb10 100644 --- a/usr/src/cmd/man/Makefile +++ b/usr/src/cmd/man/Makefile @@ -14,12 +14,7 @@ # Copyright 2014 Garrett D'Amore <garrett@damore.org> # -PROG= man -LINKS= apropos whatis catman -LIBLINKS = makewhatis -OBJS= makewhatis.o man.o stringlist.o -SRCS= $(OBJS:%.o=%.c) - +include Makefile.com include $(SRC)/cmd/Makefile.cmd CFLAGS += $(CCVERBOSE) diff --git a/usr/src/cmd/man/Makefile.com b/usr/src/cmd/man/Makefile.com new file mode 100644 index 0000000000..8f1b3adf7d --- /dev/null +++ b/usr/src/cmd/man/Makefile.com @@ -0,0 +1,23 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2012 Nexenta Systems, Inc. All rights reserved. +# Copyright 2014 Garrett D'Amore <garrett@damore.org> +# + +PROG= man +LINKS= apropos whatis catman +LIBLINKS = makewhatis +OBJS= makewhatis.o man.o stringlist.o +SRCS= $(OBJS:%.o=%.c) + + diff --git a/usr/src/cmd/mdb/Makefile.common b/usr/src/cmd/mdb/Makefile.common index 4e282bf001..b78836c741 100644 --- a/usr/src/cmd/mdb/Makefile.common +++ b/usr/src/cmd/mdb/Makefile.common @@ -38,7 +38,8 @@ COMMON_MODULES_PROC = \ libuutil \ libzpool \ mdb_ds \ - mdb_test + mdb_test \ + v8 # # MDB modules used for debugging user processes which are only 32-bit diff --git a/usr/src/cmd/mdb/Makefile.module b/usr/src/cmd/mdb/Makefile.module index a1b66e05fb..d613d1ac2a 100644 --- a/usr/src/cmd/mdb/Makefile.module +++ b/usr/src/cmd/mdb/Makefile.module @@ -23,6 +23,7 @@ # Use is subject to license terms. # # Copyright (c) 2013 by Delphix. All rights reserved. +# Copyright 2015, Joyent, Inc. # .KEEP_STATE: @@ -31,9 +32,10 @@ include $(SRC)/cmd/mdb/Makefile.tools $(KMOD_SOURCES_DIFFERENT)KMODSRCS = $(MODSRCS) +$(KMOD_SOURCES_DIFFERENT)KMODASMSRCS = $(MODASMSRCS) -MODOBJS = $(MODSRCS:%.c=dmod/%.o) -KMODOBJS = $(KMODSRCS:%.c=kmod/%.o) +MODOBJS = $(MODSRCS:%.c=dmod/%.o) $(MODASMSRCS:%.s=dmod/%.o) +KMODOBJS = $(KMODSRCS:%.c=kmod/%.o) $(KMODASMSRCS:%.s=kmod/%.o) MODNAME = $(MODULE:%.so=%) KMODULE = $(MODNAME) @@ -102,6 +104,8 @@ CFLAGS64 += $(CCVERBOSE) CPPFLAGS += $($(MDBTGT)_TGTFLAGS) -I../../../common LDFLAGS += $(ZTEXT) LDFLAGS64 += $(ZTEXT) +ASFLAGS += -P +AS_CPPFLAGS += -D_ASM # Module type-specific compiler flags $(MODOBJS) := CFLAGS += $(C_BIGPICFLAGS) $(XREGSFLAG) @@ -209,18 +213,34 @@ dmod/%.o kmod/%.o: %.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +dmod/%.o kmod%.o: %.s + $(COMPILE.s) -o $@ $< + $(CTFCONVERT_O) + dmod/%.o kmod/%.o: ../%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +dmod/%.o kmod%.o: ../%.s + $(COMPILE.s) -o $@ $< + $(CTFCONVERT_O) + dmod/%.o kmod/%.o: ../../../common/modules/$(MODNAME)/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +dmod/%.o kmod%.o: ../../../common/modules/$(MODNAME)/%.s + $(COMPILE.s) -o $@ $< + $(CTFCONVERT_O) + dmod/%.o kmod/%.o: $$(MODSRCS_DIR)/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +dmod/%.o kmod%.o: $$(MODSRCS_DIR)/%.s + $(COMPILE.s) -o $@ $< + $(CTFCONVERT_O) + # # Lint # diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c b/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c index bfa1d1d072..24da420f6f 100644 --- a/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c +++ b/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c @@ -22,10 +22,9 @@ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2015, Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * libctf open/close interposition layer * @@ -101,3 +100,10 @@ mdb_ctf_close(ctf_file_t *fp) { ctf_close(fp); } + +/*ARGSUSED*/ +int +mdb_ctf_write(const char *file, ctf_file_t *fp) +{ + return (ENOTSUP); +} diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c index 81ac1eacfc..f39fb83c9b 100644 --- a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c +++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c @@ -742,10 +742,10 @@ kmdb_prom_debugger_exit(void) mdb.m_pio = NULL; } -#ifdef DEBUG /* - * The prom_* files use ASSERT, which is #defined as assfail(). - * We need to redirect that to our assert function. + * The prom_* files use ASSERT, which is #defined as assfail(). We need to + * redirect that to our assert function. This is also used by the various STAND + * libraries. */ int kmdb_prom_assfail(const char *assertion, const char *file, int line) @@ -754,7 +754,6 @@ kmdb_prom_assfail(const char *assertion, const char *file, int line) /*NOTREACHED*/ return (0); } -#endif /* * Begin the initialization of the debugger/PROM interface. Initialization is diff --git a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c index 0a211fcd22..e8c703e754 100644 --- a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c +++ b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_debug.h> #include <mdb/mdb.h> @@ -67,6 +65,13 @@ ctf_fdopen(int fd, int *errp) /*ARGSUSED*/ ctf_file_t * +ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp) +{ + return (ctf_set_open_errno(errp, ENOTSUP)); +} + +/*ARGSUSED*/ +ctf_file_t * ctf_open(const char *filename, int *errp) { return (ctf_set_open_errno(errp, ENOTSUP)); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c index e9829bd6c2..95b216cfbe 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c @@ -26,7 +26,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. - * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright (c) 2015 Joyent, Inc. All rights reserved. * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> */ @@ -2977,7 +2977,7 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = { { "status", NULL, "print summary of current target", cmd_notsup }, { "term", NULL, "display current terminal type", cmd_term }, { "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset }, - { "typedef", "[-c model | -d | -l | -r file ] [type] [name]", + { "typedef", "[-c model | -d | -l | -r file | -w file ] [type] [name]", "create synthetic types", cmd_typedef, cmd_typedef_help }, { "unset", "[name ...]", "unset variables", cmd_unset }, { "vars", "[-npt]", "print listing of variables", cmd_vars }, diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c index 072ac9ef12..358853e660 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c @@ -24,7 +24,7 @@ */ /* * Copyright (c) 2013 by Delphix. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. */ #include <mdb/mdb_ctf.h> @@ -37,6 +37,7 @@ #include <libctf.h> #include <string.h> +#include <limits.h> typedef struct tnarg { mdb_tgt_t *tn_tgt; /* target to use for lookup */ @@ -781,8 +782,9 @@ mdb_ctf_enum_iter(mdb_ctf_id_t id, mdb_ctf_enum_f *cb, void *data) /* * callback proxy for mdb_ctf_type_iter */ +/* ARGSUSED */ static int -type_iter_cb(ctf_id_t type, void *data) +type_iter_cb(ctf_id_t type, boolean_t root, void *data) { type_iter_t *tip = data; mdb_ctf_id_t id; @@ -812,7 +814,7 @@ mdb_ctf_type_iter(const char *object, mdb_ctf_type_f *cb, void *data) ti.ti_arg = data; ti.ti_fp = fp; - if ((ret = ctf_type_iter(fp, type_iter_cb, &ti)) == CTF_ERR) + if ((ret = ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti)) == CTF_ERR) return (set_errno(ctf_to_errno(ctf_errno(fp)))); return (ret); @@ -1780,7 +1782,6 @@ mdb_ctf_synthetics_create_base(int kind) return (0); discard: - err = set_errno(ctf_to_errno(ctf_errno(cp))); (void) ctf_discard(cp); return (err); } @@ -1939,7 +1940,7 @@ mdb_ctf_add_member(const mdb_ctf_id_t *p, const char *name, return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth)))); } - id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid); + id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid, ULONG_MAX); if (id == CTF_ERR) { mdb_dprintf(MDB_DBG_CTF, "failed to add member %s: %s\n", name, ctf_errmsg(ctf_errno(mdb.m_synth))); @@ -2040,7 +2041,7 @@ mdb_ctf_add_pointer(const mdb_ctf_id_t *p, mdb_ctf_id_t *rid) } - id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, id); + id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, NULL, id); if (id == CTF_ERR) { mdb_dprintf(MDB_DBG_CTF, "failed to add pointer: %s\n", ctf_errmsg(ctf_errno(mdb.m_synth))); @@ -2088,6 +2089,7 @@ mdb_ctf_type_delete(const mdb_ctf_id_t *id) return (0); } +/* ARGSUSED */ static int mdb_ctf_synthetics_file_cb(mdb_ctf_id_t id, void *arg) { @@ -2125,7 +2127,7 @@ mdb_ctf_synthetics_from_file(const char *file) ti.ti_fp = fp; ti.ti_arg = syn; ti.ti_cb = mdb_ctf_synthetics_file_cb; - if (ctf_type_iter(fp, type_iter_cb, &ti) == CTF_ERR) { + if (ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti) == CTF_ERR) { ret = set_errno(ctf_to_errno(ctf_errno(fp))); mdb_warn("failed to add types"); goto cleanup; @@ -2142,3 +2144,30 @@ cleanup: (void) ctf_discard(syn); return (ret); } + +int +mdb_ctf_synthetics_to_file(const char *file) +{ + int err; + ctf_file_t *fp = mdb.m_synth; + + if (fp == NULL) { + mdb_warn("synthetic types are disabled, not writing " + "anything\n"); + return (DCMD_ERR); + } + + err = mdb_ctf_write(file, fp); + if (err != 0) { + if (err == CTF_ERR) { + (void) set_errno(ctf_to_errno(ctf_errno(fp))); + } else { + (void) set_errno(err); + } + err = DCMD_ERR; + } else { + err = DCMD_OK; + } + + return (err); +} diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.h b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h index 2396145299..85e60494d0 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.h @@ -24,7 +24,7 @@ */ /* * Copyright (c) 2013 by Delphix. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. */ #ifndef _MDB_CTF_H @@ -151,10 +151,12 @@ extern int mdb_ctf_readsym(void *, const char *, const char *, uint_t); extern ctf_file_t *mdb_ctf_open(const char *, int *); /* Internal */ extern ctf_file_t *mdb_ctf_bufopen(const void *, size_t, /* Internal */ const void *, Shdr *, const void *, Shdr *, int *); +extern int mdb_ctf_write(const char *, ctf_file_t *fp); /* Internal */ extern void mdb_ctf_close(ctf_file_t *fp); /* Internal */ extern int mdb_ctf_synthetics_init(void); /* Internal */ extern void mdb_ctf_synthetics_fini(void); /* Internal */ extern int mdb_ctf_synthetics_from_file(const char *); /* Internal */ +extern int mdb_ctf_synthetics_to_file(const char *); /* Internal */ #endif diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c index 730a9a4599..525cb5e88a 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c @@ -22,10 +22,9 @@ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2015, Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * libctf open/close interposition layer * @@ -36,6 +35,11 @@ #include <mdb/mdb_ctf.h> #include <libctf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> ctf_file_t * mdb_ctf_open(const char *filename, int *errp) @@ -48,3 +52,22 @@ mdb_ctf_close(ctf_file_t *fp) { ctf_close(fp); } + +int +mdb_ctf_write(const char *filename, ctf_file_t *fp) +{ + int fd, ret; + + if ((fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) + return (errno); + + if (ctf_write(fp, fd) == CTF_ERR) { + (void) close(fd); + return (CTF_ERR); + } + + ret = close(fd); + if (ret != 0) + ret = errno; + return (ret); +} diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.c b/usr/src/cmd/mdb/common/mdb/mdb_debug.c index d373d3726e..a158864a28 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_debug.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_debug.h> #include <mdb/mdb_err.h> #include <mdb/mdb_io.h> @@ -157,7 +155,6 @@ mdb_dmode(uint_t bits) mdb.m_debug = bits; } -#ifdef DEBUG int mdb_dassert(const char *expr, const char *file, int line) { @@ -165,7 +162,6 @@ mdb_dassert(const char *expr, const char *file, int line) /*NOTREACHED*/ return (0); } -#endif /* * Function to convert mdb longjmp codes (see <mdb/mdb.h>) into a string for diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.h b/usr/src/cmd/mdb/common/mdb/mdb_debug.h index cf3b97b499..f889238e4f 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_debug.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.h @@ -27,8 +27,6 @@ #ifndef _MDB_DEBUG_H #define _MDB_DEBUG_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -66,8 +64,8 @@ extern void mdb_dmode(uint_t); extern const char *mdb_err2str(int); -#ifdef DEBUG extern int mdb_dassert(const char *, const char *, int); +#ifdef DEBUG #define ASSERT(x) ((void)((x) || mdb_dassert(#x, __FILE__, __LINE__))) #else #define ASSERT(x) diff --git a/usr/src/cmd/mdb/common/mdb/mdb_demangle.c b/usr/src/cmd/mdb/common/mdb/mdb_demangle.c index 7ab7d19716..27c8a1d21d 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_demangle.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_demangle.c @@ -24,7 +24,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ #include <mdb/mdb_modapi.h> #include <mdb/mdb_demangle.h> @@ -263,7 +265,8 @@ int cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { mdb_demangler_t *dmp = mdb.m_demangler; - const char *path = LIB_DEMANGLE; + const char *path; + char buf[MAXPATHLEN]; if (argc > 1 || (argc > 0 && argv->a_type != MDB_TYPE_STRING)) return (DCMD_USAGE); @@ -272,6 +275,10 @@ cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (dmp != NULL) mdb_dem_unload(mdb.m_demangler); path = argv->a_un.a_str; + } else { + (void) snprintf(buf, MAXPATHLEN, + "%s/%s", mdb.m_root, LIB_DEMANGLE); + path = buf; } if (dmp != NULL && argc == 0 && !(mdb.m_flags & MDB_FL_DEMANGLE)) { diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c index 90e048bb82..a30ee45b7e 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_main.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c @@ -25,7 +25,7 @@ */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -51,6 +51,7 @@ #include <libctf.h> #include <errno.h> #include <kvm.h> +#include <zone.h> #include <mdb/mdb_lex.h> #include <mdb/mdb_debug.h> @@ -797,9 +798,15 @@ main(int argc, char *argv[], char *envp[]) if (strchr(pidarg, '/') != NULL) (void) mdb_iob_snprintf(object, MAXPATHLEN, "%s/object/a.out", pidarg); - else + else { + const char *root; + (void) mdb_iob_snprintf(object, MAXPATHLEN, - "/proc/%s/object/a.out", pidarg); + "%s/proc/%s/object/a.out", + (root = zone_get_nroot()) != NULL ? root : "", + pidarg); + } + tgt_argv[tgt_argc++] = object; tgt_argv[tgt_argc++] = pidarg; } diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h index 9d08a18c69..c6b4956b3b 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h @@ -22,7 +22,7 @@ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. - * Copyright (c) 2012 Joyent, Inc. All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. */ #ifndef _MDB_MODAPI_H @@ -71,7 +71,13 @@ extern "C" { #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif +#ifdef MDB_API_VERSION +#if (MDB_API_VERSION != 3 && MDB_API_VERSION != 4) +#error "Only modapi versions three and four are supported." +#endif +#else /* !MDB_API_VERISON */ #define MDB_API_VERSION 4 /* Current API version number */ +#endif /* MDB_API_VERISON */ /* * Debugger command function flags: @@ -85,11 +91,6 @@ extern "C" { #define DCMD_HDRSPEC(fl) (((fl) & DCMD_LOOPFIRST) || !((fl) & DCMD_LOOP)) /* - * Debugger tab command function flags - */ -#define DCMD_TAB_SPACE 0x01 /* Tab cb invoked with trailing space */ - -/* * Debugger command function return values: */ #define DCMD_OK 0 /* Dcmd completed successfully */ @@ -118,10 +119,18 @@ typedef struct mdb_arg { } a_un; } mdb_arg_t; +#if (MDB_API_VERSION >= 4) +/* + * Debugger tab command function flags + */ +#define DCMD_TAB_SPACE 0x01 /* Tab cb invoked with trailing space */ + typedef struct mdb_tab_cookie mdb_tab_cookie_t; -typedef int mdb_dcmd_f(uintptr_t, uint_t, int, const mdb_arg_t *); typedef int mdb_dcmd_tab_f(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *); +#endif /* MDB_API_VERSION >= 4 */ + +typedef int mdb_dcmd_f(uintptr_t, uint_t, int, const mdb_arg_t *); typedef struct mdb_dcmd { const char *dc_name; /* Command name */ @@ -129,7 +138,9 @@ typedef struct mdb_dcmd { const char *dc_descr; /* Description */ mdb_dcmd_f *dc_funcp; /* Command function */ void (*dc_help)(void); /* Command help function (or NULL) */ +#if (MDB_API_VERSION >= 4) mdb_dcmd_tab_f *dc_tabp; /* Tab completion function */ +#endif } mdb_dcmd_t; #define WALK_ERR -1 /* Walk fatal error (terminate walk) */ @@ -346,6 +357,7 @@ typedef void (*mdb_callback_f)(void *); extern void *mdb_callback_add(int, mdb_callback_f, void *); extern void mdb_callback_remove(void *); +#if (MDB_API_VERSION >= 4) #define MDB_TABC_ALL_TYPES 0x1 /* Include array types in type output */ #define MDB_TABC_MEMBERS 0x2 /* Tab comp. types with members */ #define MDB_TABC_NOPOINT 0x4 /* Tab comp. everything but pointers */ @@ -370,6 +382,7 @@ extern int mdb_tab_typename(int *, const mdb_arg_t **, char *buf, size_t len); */ extern int mdb_tab_complete_mt(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *); +#endif /* MDB_API_VERSION >= 4 */ extern size_t strlcat(char *, const char *, size_t); extern char *strcat(char *, const char *); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.c b/usr/src/cmd/mdb/common/mdb/mdb_print.c index 56275fdded..3be869c2c0 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_print.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c @@ -2105,10 +2105,10 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, /* * This is the reason that tab completion was created. We're going to go - * along and walk the delimiters until we find something a member that - * we don't recognize, at which point we'll try and tab complete it. - * Note that ::print takes multiple args, so this is going to operate on - * whatever the last arg that we have is. + * along and walk the delimiters until we find something in a member + * that we don't recognize, at which point we'll try and tab complete + * it. Note that ::print takes multiple args, so this is going to + * operate on whatever the last arg that we have is. */ if (mdb_ctf_lookup_by_name(tn, &id) != 0) return (1); @@ -2118,11 +2118,11 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, delim = parse_delimiter(&start); /* - * If we hit the case where we actually have no delimiters, than we need + * If we hit the case where we actually have no delimiters, then we need * to make sure that we properly set up the fields the loops would. */ if (delim == MEMBER_DELIM_DONE) - (void) mdb_snprintf(member, sizeof (member), "%s", start); + (void) mdb_snprintf(member, sizeof (member), start); while (delim != MEMBER_DELIM_DONE) { switch (delim) { @@ -2179,7 +2179,7 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, /* * We are going to try to resolve this name as a member. There - * are a few two different questions that we need to answer. The + * are a two different questions that we need to answer. The * first is do we recognize this member. The second is are we at * the end of the string. If we encounter a member that we don't * recognize before the end, then we have to error out and can't @@ -2209,6 +2209,7 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, * already have in rid. */ return (mdb_tab_complete_member_by_id(mcp, rid, member)); + } int @@ -2538,8 +2539,7 @@ print_help(void) } static int -printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, - boolean_t sign) +printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, int sign) { ssize_t size; mdb_ctf_id_t base; @@ -2557,7 +2557,7 @@ printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, } u; if (mdb_ctf_type_resolve(id, &base) == -1) { - mdb_warn("could not resolve type"); + mdb_warn("could not resolve type\n"); return (DCMD_ABORT); } @@ -2795,7 +2795,6 @@ printf_string(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt) if (size != 1) { mdb_warn("string format specifier requires " "an array of characters\n"); - return (DCMD_ABORT); } bzero(buf, sizeof (buf)); @@ -2929,7 +2928,7 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (argc == 0 || argv[0].a_type != MDB_TYPE_STRING) { mdb_warn("expected a format string\n"); - return (DCMD_USAGE); + return (DCMD_ABORT); } /* @@ -2938,12 +2937,6 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * subset of mdb_printf() format strings that we allow. */ fmt = argv[0].a_un.a_str; - /* - * 'dest' must be large enough to hold a copy of the format string, - * plus a NUL and up to 2 additional characters for each conversion - * in the format string. This gives us a bloat factor of 5/2 ~= 3. - * e.g. "%d" (strlen of 2) --> "%lld\0" (need 5 bytes) - */ dest = mdb_zalloc(strlen(fmt) * 3, UM_SLEEP | UM_GC); fmts = mdb_zalloc(strlen(fmt) * sizeof (char *), UM_SLEEP | UM_GC); funcs = mdb_zalloc(strlen(fmt) * sizeof (void *), UM_SLEEP | UM_GC); @@ -3126,22 +3119,22 @@ static char _mdb_printf_help[] = "\n" " %% Prints the '%' symbol.\n" " %a Prints the member in symbolic form.\n" -" %d Prints the member as a decimal integer. If the member is a signed\n" +" %d Prints the member as a decimal integer. If the member is a signed\n" " integer type, the output will be signed.\n" " %H Prints the member as a human-readable size.\n" " %I Prints the member as an IPv4 address (must be 32-bit integer type).\n" " %N Prints the member as an IPv6 address (must be of type in6_addr_t).\n" " %o Prints the member as an unsigned octal integer.\n" " %p Prints the member as a pointer, in hexadecimal.\n" -" %q Prints the member in signed octal. Honk if you ever use this!\n" -" %r Prints the member as an unsigned value in the current output radix.\n" -" %R Prints the member as a signed value in the current output radix.\n" +" %q Prints the member in signed octal. Honk if you ever use this!\n" +" %r Prints the member as an unsigned value in the current output radix. \n" +" %R Prints the member as a signed value in the current output radix. \n" " %s Prints the member as a string (requires a pointer or an array of\n" " characters).\n" " %u Prints the member as an unsigned decimal integer.\n" " %x Prints the member in hexadecimal.\n" " %X Prints the member in hexadecimal, using the characters A-F as the\n" -" digits for the values 10-15.\n" +" digits for the values 10-15. \n" " %Y Prints the member as a time_t as the string " "'year month day HH:MM:SS'.\n" "\n" @@ -3154,13 +3147,13 @@ static char _mdb_printf_help[] = "\n" "The following flag specifers are recognized by ::printf:\n" "\n" -" %- Left-justify the output within the specified field width. If the\n" +" %- Left-justify the output within the specified field width. If the\n" " width of the output is less than the specified field width, the\n" -" output will be padded with blanks on the right-hand side. Without\n" +" output will be padded with blanks on the right-hand side. Without\n" " %-, values are right-justified by default.\n" "\n" " %0 Zero-fill the output field if the output is right-justified and the\n" -" width of the output is less than the specified field width. Without\n" +" width of the output is less than the specified field width. Without\n" " %0, right-justified values are prepended with blanks in order to\n" " fill the field.\n" "\n" diff --git a/usr/src/cmd/mdb/common/mdb/mdb_tab.c b/usr/src/cmd/mdb/common/mdb/mdb_tab.c index af32623470..66bd18586e 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_tab.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_tab.c @@ -388,11 +388,6 @@ mdb_tab_size(mdb_tab_cookie_t *mcp) return (mdb_nv_size(&mcp->mtc_nv)); } -/* - * Determine whether the specified name is a valid tab completion for - * the given command. If the name is a valid tab completion then - * it will be saved in the mdb_tab_cookie_t. - */ void mdb_tab_insert(mdb_tab_cookie_t *mcp, const char *name) { @@ -508,31 +503,18 @@ tab_complete_type(mdb_ctf_id_t id, void *arg) mdb_tab_cookie_t *mcp = arg; uint_t flags = (uint_t)(uintptr_t)mcp->mtc_cba; - /* - * CTF data includes types that mdb commands don't understand. Before - * we resolve the actual type prune any entry that is a type we - * don't care about. - */ - switch (mdb_ctf_type_kind(id)) { - case CTF_K_CONST: - case CTF_K_RESTRICT: - case CTF_K_VOLATILE: - return (0); - } - if (mdb_ctf_type_resolve(id, &rid) != 0) return (1); rkind = mdb_ctf_type_kind(rid); - - if ((flags & MDB_TABC_MEMBERS) && rkind != CTF_K_STRUCT && + if (flags & MDB_TABC_MEMBERS && rkind != CTF_K_STRUCT && rkind != CTF_K_UNION) return (0); - if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER) + if (flags & MDB_TABC_NOPOINT && rkind == CTF_K_POINTER) return (0); - if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY) + if (flags & MDB_TABC_NOARRAY && rkind == CTF_K_ARRAY) return (0); (void) mdb_ctf_type_name(id, buf, sizeof (buf)); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_termio.c b/usr/src/cmd/mdb/common/mdb/mdb_termio.c index 5e36cda7ad..dd430bff10 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_termio.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_termio.c @@ -26,7 +26,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. - * Copyright (c) 2012 Joyent, Inc. All rights reserved. + * Copyright 2015 Joyent, Inc. */ /* @@ -93,8 +93,23 @@ #define KEY_ESC (0x01b) /* Escape key code */ #define KEY_DEL (0x07f) /* ASCII DEL key code */ -#define META(c) ((c) | 0x080) /* Convert 'x' to 'M-x' */ -#define KPAD(c) ((c) | 0x100) /* Convert 'x' to 'ESC-[-x' */ +/* + * These macros support the use of various ranges within the "tio_keymap" + * member of "termio_data_t" objects. This array maps from an input byte, or + * special control code, to the appropriate terminal handling callback. The + * array has KEY_MAX (0x1ff) entries, partitioned as follows: + * + * 0 - 7f 7-bit ASCII byte + * 80 - ff META() ASCII byte with Meta key modifier + * 100 - 119 KPAD() Alphabetic character received as part of a single-byte + * cursor control sequence, e.g. ESC [ A + * 11a - 123 FKEY() Numeric character received as part of a function key + * control sequence, e.g. ESC [ 4 ~ + * 124 - 1ff Unused + */ +#define META(c) (((c) & 0x7f) | 0x80) +#define KPAD(c) (((c) < 'A' || (c) > 'Z') ? 0 : ((c) - 'A' + 0x100)) +#define FKEY(c) (((c) < '0' || (c) > '9') ? 0 : ((c) - '0' + 0x11a)) /* * These macros allow for composition of control sequences for xterm and other @@ -202,7 +217,7 @@ typedef struct termio_data { mdb_iob_t *tio_out; /* I/o buffer for terminal output */ mdb_iob_t *tio_in; /* I/o buffer for terminal input */ mdb_iob_t *tio_link; /* I/o buffer to resize on WINCH */ - keycb_t tio_keymap[KEY_MAX]; /* Keymap (callback functions) */ + keycb_t tio_keymap[KEY_MAX]; /* Keymap (see comments atop file) */ mdb_cmdbuf_t tio_cmdbuf; /* Editable command-line buffer */ struct termios tio_ptios; /* Parent terminal settings */ struct termios tio_ctios; /* Child terminal settings */ @@ -385,7 +400,7 @@ termio_read(mdb_io_t *io, void *buf, size_t nbytes) mdb_bool_t esc = FALSE, pad = FALSE; ssize_t rlen = 0; - int c; + int c, fkey = 0; const char *s; size_t len; @@ -471,8 +486,42 @@ char_loop: } if (pad) { - c = KPAD(CTRL(c)); pad = FALSE; + + if ((fkey = FKEY(c)) != 0) { + /* + * Some terminals send a multibyte control + * sequence for particular function keys. + * These sequences are of the form: + * + * ESC [ n ~ + * + * where "n" is a numeric character from + * 0 to 9. + */ + goto char_loop; + } + + if ((c = KPAD(c)) == 0) { + /* + * This was not a valid keypad control + * sequence. + */ + goto char_loop; + } + } + + if (fkey != 0) { + if (c == '~') { + /* + * This is a valid special function key + * sequence. Use the value we stashed + * earlier. + */ + c = fkey; + } + + fkey = 0; } len = td->tio_cmdbuf.cmd_buflen + td->tio_promptlen; @@ -1486,10 +1535,20 @@ mdb_termio_create(const char *name, mdb_io_t *rio, mdb_io_t *wio) td->tio_keymap[CTRL('d')] = termio_delchar; td->tio_keymap[CTRL('?')] = termio_widescreen; - td->tio_keymap[KPAD(CTRL('A'))] = termio_prevhist; - td->tio_keymap[KPAD(CTRL('B'))] = termio_nexthist; - td->tio_keymap[KPAD(CTRL('C'))] = termio_fwdchar; - td->tio_keymap[KPAD(CTRL('D'))] = termio_backchar; + td->tio_keymap[KPAD('A')] = termio_prevhist; + td->tio_keymap[KPAD('B')] = termio_nexthist; + td->tio_keymap[KPAD('C')] = termio_fwdchar; + td->tio_keymap[KPAD('D')] = termio_backchar; + + /* + * Many modern terminal emulators treat the "Home" and "End" keys on a + * PC keyboard as cursor keys. Some others use a multibyte function + * key control sequence. We handle both styles here: + */ + td->tio_keymap[KPAD('H')] = termio_home; + td->tio_keymap[FKEY('1')] = termio_home; + td->tio_keymap[KPAD('F')] = termio_end; + td->tio_keymap[FKEY('4')] = termio_end; /* * We default both ASCII BS and DEL to termio_backspace for safety. We diff --git a/usr/src/cmd/mdb/common/mdb/mdb_typedef.c b/usr/src/cmd/mdb/common/mdb/mdb_typedef.c index be6411596c..096411a1b7 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_typedef.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_typedef.c @@ -10,7 +10,7 @@ */ /* - * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright (c) 2015 Joyent, Inc. All rights reserved. */ /* @@ -556,7 +556,18 @@ typedef_readfile(const char *file) ret = mdb_ctf_synthetics_from_file(file); if (ret != DCMD_OK) - mdb_warn("failed to create synthetics from file\n"); + mdb_warn("failed to create synthetics from file %s\n", file); + return (ret); +} + +static int +typedef_writefile(const char *file) +{ + int ret; + + ret = mdb_ctf_synthetics_to_file(file); + if (ret != DCMD_OK) + mdb_warn("failed to write synthetics to file %s", file); return (ret); } @@ -567,7 +578,7 @@ cmd_typedef(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_ctf_id_t id; int i; int destroy = 0, list = 0; - const char *cmode = NULL, *rfile = NULL; + const char *cmode = NULL, *rfile = NULL, *wfile = NULL; const char *dst, *src; char *dup; parse_root_t *pr; @@ -579,7 +590,8 @@ cmd_typedef(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 'd', MDB_OPT_SETBITS, TRUE, &destroy, 'l', MDB_OPT_SETBITS, TRUE, &list, 'c', MDB_OPT_STR, &cmode, - 'r', MDB_OPT_STR, &rfile, NULL); + 'r', MDB_OPT_STR, &rfile, + 'w', MDB_OPT_STR, &wfile, NULL); argc -= i; argv += i; @@ -596,10 +608,13 @@ cmd_typedef(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) i++; if (rfile != NULL) i++; + if (wfile != NULL) + i++; if (i > 1) return (DCMD_USAGE); - if ((destroy || cmode != NULL || list || rfile != NULL) && argc != 0) + if ((destroy || cmode != NULL || list || rfile != NULL || + wfile != NULL) && argc != 0) return (DCMD_USAGE); if (destroy) @@ -614,6 +629,9 @@ cmd_typedef(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (rfile) return (typedef_readfile(rfile)); + if (wfile) + return (typedef_writefile(wfile)); + if (argc < 2) return (DCMD_USAGE); @@ -702,10 +720,16 @@ static char typedef_desc[] = " o packed structures (all structures currently use their natural alignment)\n" "\n" "::typedef also allows you to read type definitions from a file. Definitions\n" -"can be read from any ELF file that has a CTF section that libctf can parse.\n" +"can be read from any ELF file that has a CTF section that libctf can parse\n" +"or any raw CTF data files, such as those that can be created with ::typedef.\n" "You can check if a file has such a section with elfdump(1). If a binary or\n" "core dump does not have any type information, but you do have it elsewhere,\n" "then you can use ::typedef -r to read in that type information.\n" +"\n" +"All built up definitions may be exported as a valid CTF container that can\n" +"be used again with ::typedef -r or anything that uses libctf. To write them\n" +"out, use ::typedef -w and specify the name of a file. For more information\n" +"on the CTF file format, see ctf(4).\n" "\n"; static char typedef_opts[] = @@ -718,6 +742,7 @@ static char typedef_opts[] = " -d delete all synthetic types\n" " -l list all synthetic types\n" " -r file import type definitions (CTF) from another ELF file\n" +" -w file write all type definitions out to file\n" "\n"; static char typedef_examps[] = @@ -729,6 +754,7 @@ static char typedef_examps[] = " ::typedef \"struct list { struct list *l_next; struct list *l_prev; }\" " "list_t\n" " ::typedef -r /var/tmp/qemu-system-x86_64\n" +" ::typedef -w defs.ctf" "\n"; static char typedef_intrins[] = diff --git a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c index 7858866a01..50ad2c3497 100644 --- a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c +++ b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* @@ -433,6 +433,7 @@ dtracemdb_bufsnap(dtrace_buffer_t *which, dtrace_bufdesc_t *desc) desc->dtbd_size = bufsize; desc->dtbd_drops = buf.dtb_drops; desc->dtbd_errors = buf.dtb_errors; + desc->dtbd_timestamp = gethrtime(); return (0); } diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c index f6a9a02193..152e52b790 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c @@ -21,7 +21,7 @@ /* * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013 Joyent, Inc. All rights reserved. + * Copyright 2015 Joyent, Inc. * Copyright (c) 2013 by Delphix. All rights reserved. */ @@ -113,10 +113,6 @@ */ #define NINTR 16 -#define KILOS 10 -#define MEGS 20 -#define GIGS 30 - #ifndef STACK_BIAS #define STACK_BIAS 0 #endif @@ -169,6 +165,88 @@ ps_threadprint(uintptr_t addr, const void *data, void *private) return (WALK_NEXT); } +static int +pflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + proc_t pr; + struct pid pid; + + static const mdb_bitmask_t p_flag_bits[] = { + { "SSYS", SSYS, SSYS }, + { "SEXITING", SEXITING, SEXITING }, + { "SITBUSY", SITBUSY, SITBUSY }, + { "SFORKING", SFORKING, SFORKING }, + { "SWATCHOK", SWATCHOK, SWATCHOK }, + { "SKILLED", SKILLED, SKILLED }, + { "SSCONT", SSCONT, SSCONT }, + { "SZONETOP", SZONETOP, SZONETOP }, + { "SEXTKILLED", SEXTKILLED, SEXTKILLED }, + { "SUGID", SUGID, SUGID }, + { "SEXECED", SEXECED, SEXECED }, + { "SJCTL", SJCTL, SJCTL }, + { "SNOWAIT", SNOWAIT, SNOWAIT }, + { "SVFORK", SVFORK, SVFORK }, + { "SVFWAIT", SVFWAIT, SVFWAIT }, + { "SEXITLWPS", SEXITLWPS, SEXITLWPS }, + { "SHOLDFORK", SHOLDFORK, SHOLDFORK }, + { "SHOLDFORK1", SHOLDFORK1, SHOLDFORK1 }, + { "SCOREDUMP", SCOREDUMP, SCOREDUMP }, + { "SMSACCT", SMSACCT, SMSACCT }, + { "SLWPWRAP", SLWPWRAP, SLWPWRAP }, + { "SAUTOLPG", SAUTOLPG, SAUTOLPG }, + { "SNOCD", SNOCD, SNOCD }, + { "SHOLDWATCH", SHOLDWATCH, SHOLDWATCH }, + { "SMSFORK", SMSFORK, SMSFORK }, + { "SDOCORE", SDOCORE, SDOCORE }, + { NULL, 0, 0 } + }; + + static const mdb_bitmask_t p_pidflag_bits[] = { + { "CLDPEND", CLDPEND, CLDPEND }, + { "CLDCONT", CLDCONT, CLDCONT }, + { "CLDNOSIGCHLD", CLDNOSIGCHLD, CLDNOSIGCHLD }, + { "CLDWAITPID", CLDWAITPID, CLDWAITPID }, + { NULL, 0, 0 } + }; + + static const mdb_bitmask_t p_proc_flag_bits[] = { + { "P_PR_TRACE", P_PR_TRACE, P_PR_TRACE }, + { "P_PR_PTRACE", P_PR_PTRACE, P_PR_PTRACE }, + { "P_PR_FORK", P_PR_FORK, P_PR_FORK }, + { "P_PR_LOCK", P_PR_LOCK, P_PR_LOCK }, + { "P_PR_ASYNC", P_PR_ASYNC, P_PR_ASYNC }, + { "P_PR_EXEC", P_PR_EXEC, P_PR_EXEC }, + { "P_PR_BPTADJ", P_PR_BPTADJ, P_PR_BPTADJ }, + { "P_PR_RUNLCL", P_PR_RUNLCL, P_PR_RUNLCL }, + { "P_PR_KILLCL", P_PR_KILLCL, P_PR_KILLCL }, + { NULL, 0, 0 } + }; + + if (!(flags & DCMD_ADDRSPEC)) { + if (mdb_walk_dcmd("proc", "pflags", argc, argv) == -1) { + mdb_warn("can't walk 'proc'"); + return (DCMD_ERR); + } + return (DCMD_OK); + } + + if (mdb_vread(&pr, sizeof (pr), addr) == -1 || + mdb_vread(&pid, sizeof (pid), (uintptr_t)pr.p_pidp) == -1) { + mdb_warn("cannot read proc_t or pid"); + return (DCMD_ERR); + } + + mdb_printf("%p [pid %d]:\n", addr, pid.pid_id); + mdb_printf("\tp_flag: %08x <%b>\n", pr.p_flag, pr.p_flag, + p_flag_bits); + mdb_printf("\tp_pidflag: %08x <%b>\n", pr.p_pidflag, pr.p_pidflag, + p_pidflag_bits); + mdb_printf("\tp_proc_flag: %08x <%b>\n", pr.p_proc_flag, pr.p_proc_flag, + p_proc_flag_bits); + + return (DCMD_OK); +} + int ps(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -1904,24 +1982,24 @@ typedef struct datafmt { } datafmt_t; static datafmt_t kmemfmt[] = { - { "cache ", "name ", - "-------------------------", "%-25s " }, - { " buf", " size", "------", "%6u " }, - { " buf", "in use", "------", "%6u " }, - { " buf", " total", "------", "%6u " }, - { " memory", " in use", "----------", "%10lu%c " }, - { " alloc", " succeed", "---------", "%9u " }, - { "alloc", " fail", "-----", "%5u " }, + { "cache ", "name ", + "------------------------------", "%-30s " }, + { " buf", " size", "-----", "%5H " }, + { " buf", " in use", "---------", "%9u " }, + { " buf", " total", "---------", "%9u " }, + { "memory", "in use", "------", "%6lH " }, + { " alloc", " succeed", "----------", "%10u " }, + { "alloc", " fail", "-----", "%5u" }, { NULL, NULL, NULL, NULL } }; static datafmt_t vmemfmt[] = { - { "vmem ", "name ", - "-------------------------", "%-*s " }, - { " memory", " in use", "----------", "%9llu%c " }, - { " memory", " total", "-----------", "%10llu%c " }, - { " memory", " import", "----------", "%9llu%c " }, - { " alloc", " succeed", "---------", "%9llu " }, + { "vmem ", "name ", + "------------------------------", "%-*s " }, + { " memory", " in use", "---------", "%9llH " }, + { " memory", " total", "----------", "%10llH " }, + { " memory", " import", "---------", "%9llH " }, + { " alloc", " succeed", "----------", "%10llu " }, { "alloc", " fail", "-----", "%5llu " }, { NULL, NULL, NULL, NULL } }; @@ -1973,15 +2051,9 @@ typedef struct kmastat_vmem { int kv_fail; } kmastat_vmem_t; -typedef struct kmastat_args { - kmastat_vmem_t **ka_kvpp; - uint_t ka_shift; -} kmastat_args_t; - static int -kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_args_t *kap) +kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_vmem_t **kvpp) { - kmastat_vmem_t **kvpp = kap->ka_kvpp; kmastat_vmem_t *kv; datafmt_t *dfp = kmemfmt; int magsize; @@ -2022,9 +2094,7 @@ out: mdb_printf((dfp++)->fmt, cp->cache_bufsize); mdb_printf((dfp++)->fmt, total - avail); mdb_printf((dfp++)->fmt, total); - mdb_printf((dfp++)->fmt, meminuse >> kap->ka_shift, - kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' : - kap->ka_shift == KILOS ? 'K' : 'B'); + mdb_printf((dfp++)->fmt, meminuse); mdb_printf((dfp++)->fmt, alloc); mdb_printf((dfp++)->fmt, cp->cache_alloc_fail); mdb_printf("\n"); @@ -2033,9 +2103,8 @@ out: } static int -kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap) +kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_vmem_t *kv) { - kmastat_vmem_t *kv = *kap->ka_kvpp; size_t len; while (kv != NULL && kv->kv_addr != addr) @@ -2044,20 +2113,18 @@ kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap) if (kv == NULL || kv->kv_alloc == 0) return (WALK_NEXT); - len = MIN(17, strlen(v->vm_name)); + len = MIN(22, strlen(v->vm_name)); - mdb_printf("Total [%s]%*s %6s %6s %6s %10lu%c %9u %5u\n", v->vm_name, - 17 - len, "", "", "", "", - kv->kv_meminuse >> kap->ka_shift, - kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' : - kap->ka_shift == KILOS ? 'K' : 'B', kv->kv_alloc, kv->kv_fail); + mdb_printf("Total [%s]%*s %5s %9s %9s %6lH %10u %5u\n", v->vm_name, + 22 - len, "", "", "", "", + kv->kv_meminuse, kv->kv_alloc, kv->kv_fail); return (WALK_NEXT); } /*ARGSUSED*/ static int -kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp) +kmastat_vmem(uintptr_t addr, const vmem_t *v, const void *ignored) { datafmt_t *dfp = vmemfmt; const vmem_kstat_t *vkp = &v->vm_kstat; @@ -2075,16 +2142,10 @@ kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp) } mdb_printf("%*s", ident, ""); - mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name); - mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64 >> *shiftp, - *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : - *shiftp == KILOS ? 'K' : 'B'); - mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64 >> *shiftp, - *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : - *shiftp == KILOS ? 'K' : 'B'); - mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64 >> *shiftp, - *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : - *shiftp == KILOS ? 'K' : 'B'); + mdb_printf((dfp++)->fmt, 30 - ident, v->vm_name); + mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64); + mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64); + mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64); mdb_printf((dfp++)->fmt, vkp->vk_alloc.value.ui64); mdb_printf((dfp++)->fmt, vkp->vk_fail.value.ui64); @@ -2099,44 +2160,35 @@ kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { kmastat_vmem_t *kv = NULL; datafmt_t *dfp; - kmastat_args_t ka; - - ka.ka_shift = 0; - if (mdb_getopts(argc, argv, - 'k', MDB_OPT_SETBITS, KILOS, &ka.ka_shift, - 'm', MDB_OPT_SETBITS, MEGS, &ka.ka_shift, - 'g', MDB_OPT_SETBITS, GIGS, &ka.ka_shift, NULL) != argc) - return (DCMD_USAGE); for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->hdr1); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->hdr1); mdb_printf("\n"); for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->hdr2); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->hdr2); mdb_printf("\n"); for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->dashes); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes); mdb_printf("\n"); - ka.ka_kvpp = &kv; - if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &ka) == -1) { + if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &kv) == -1) { mdb_warn("can't walk 'kmem_cache'"); return (DCMD_ERR); } for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->dashes); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes); mdb_printf("\n"); - if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, &ka) == -1) { + if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, kv) == -1) { mdb_warn("can't walk 'vmem'"); return (DCMD_ERR); } for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->dashes); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes); mdb_printf("\n"); mdb_printf("\n"); @@ -2153,7 +2205,7 @@ kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("%s ", dfp->dashes); mdb_printf("\n"); - if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, &ka.ka_shift) == -1) { + if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, NULL) == -1) { mdb_warn("can't walk 'vmem'"); return (DCMD_ERR); } @@ -3867,6 +3919,7 @@ static const mdb_dcmd_t dcmds[] = { { "pid2proc", "?", "convert PID to proc_t address", pid2proc }, { "project", NULL, "display kernel project(s)", project }, { "ps", "[-fltzTP]", "list processes (and associated thr,lwp)", ps }, + { "pflags", NULL, "display various proc_t flags", pflags }, { "pgrep", "[-x] [-n | -o] pattern", "pattern match against all processes", pgrep }, { "ptree", NULL, "print process tree", ptree }, @@ -3993,8 +4046,7 @@ static const mdb_dcmd_t dcmds[] = { { "freedby", ":", "given a thread, print its freed buffers", freedby }, { "kmalog", "?[ fail | slab ]", "display kmem transaction log and stack traces", kmalog }, - { "kmastat", "[-kmg]", "kernel memory allocator stats", - kmastat }, + { "kmastat", NULL, "kernel memory allocator stats", kmastat }, { "kmausers", "?[-ef] [cache ...]", "current medium and large users " "of the kmem allocator", kmausers, kmausers_help }, { "kmem_cache", "?[-n name]", @@ -4096,6 +4148,9 @@ static const mdb_dcmd_t dcmds[] = { /* from netstack.c */ { "netstack", "", "show stack instances", netstack }, + { "netstackid2netstack", ":", + "translate a netstack id to its netstack_t", + netstackid2netstack }, /* from nvpair.c */ { NVPAIR_DCMD_NAME, NVPAIR_DCMD_USAGE, NVPAIR_DCMD_DESCR, @@ -4186,6 +4241,10 @@ static const mdb_dcmd_t dcmds[] = { pfiles_help }, /* from zone.c */ + { "zid2zone", ":", "find the zone_t with the given zone id", + zid2zone }, + { "zdid2zone", ":", "find the zone_t with the given zone debug id", + zdid2zone }, { "zone", "?[-r [-v]]", "display kernel zone(s)", zoneprt }, { "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for " "selected zones", zsd }, diff --git a/usr/src/cmd/mdb/common/modules/genunix/netstack.c b/usr/src/cmd/mdb/common/modules/genunix/netstack.c index 588bd6dbf3..d46bd85d1f 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/netstack.c +++ b/usr/src/cmd/mdb/common/modules/genunix/netstack.c @@ -21,10 +21,9 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_modapi.h> #include <mdb/mdb_ks.h> #include <mdb/mdb_ctf.h> @@ -121,3 +120,30 @@ netstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_OK); } + +static int +netstackid_lookup_cb(uintptr_t addr, const netstack_t *ns, void *arg) +{ + netstackid_t nid = *(uintptr_t *)arg; + if (ns->netstack_stackid == nid) + mdb_printf("%p\n", addr); + + return (WALK_NEXT); +} + +/*ARGSUSED*/ +int +netstackid2netstack(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + if (!(flags & DCMD_ADDRSPEC) || argc != 0) + return (DCMD_USAGE); + + if (mdb_walk("netstack", (mdb_walk_cb_t)netstackid_lookup_cb, &addr) == + -1) { + mdb_warn("failed to walk zone"); + return (DCMD_ERR); + } + + return (DCMD_OK); +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/netstack.h b/usr/src/cmd/mdb/common/modules/genunix/netstack.h index 392565caca..f5773c36c1 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/netstack.h +++ b/usr/src/cmd/mdb/common/modules/genunix/netstack.h @@ -26,8 +26,6 @@ #ifndef _NETSTACK_H #define _NETSTACK_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_modapi.h> #ifdef __cplusplus @@ -38,6 +36,7 @@ int netstack_walk_init(mdb_walk_state_t *); int netstack_walk_step(mdb_walk_state_t *); int netstack(uintptr_t, uint_t, int, const mdb_arg_t *); +int netstackid2netstack(uintptr_t, uint_t, int, const mdb_arg_t *); #ifdef __cplusplus } diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.c b/usr/src/cmd/mdb/common/modules/genunix/zone.c index 96f6b598ec..77ed2cbc48 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/zone.c +++ b/usr/src/cmd/mdb/common/modules/genunix/zone.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <mdb/mdb_param.h> @@ -33,9 +34,9 @@ #define ZONE_NAMELEN 20 #ifdef _LP64 -#define ZONE_PATHLEN 32 +#define ZONE_PATHLEN 25 #else -#define ZONE_PATHLEN 40 +#define ZONE_PATHLEN 33 #endif /* @@ -54,6 +55,56 @@ char *zone_status_names[] = { "dead" /* ZONE_IS_DEAD */ }; +static int +zid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg) +{ + zoneid_t zid = *(uintptr_t *)arg; + if (zone->zone_id == zid) + mdb_printf("%p\n", addr); + + return (WALK_NEXT); +} + +/*ARGSUSED*/ +int +zid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + if (!(flags & DCMD_ADDRSPEC) || argc != 0) + return (DCMD_USAGE); + + if (mdb_walk("zone", (mdb_walk_cb_t)zid_lookup_cb, &addr) == -1) { + mdb_warn("failed to walk zone"); + return (DCMD_ERR); + } + + return (DCMD_OK); +} + +static int +zdid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg) +{ + zoneid_t zdid = *(uintptr_t *)arg; + if (zone->zone_did == zdid) + mdb_printf("%p\n", addr); + + return (WALK_NEXT); +} + +/*ARGSUSED*/ +int +zdid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + if (!(flags & DCMD_ADDRSPEC) || argc != 0) + return (DCMD_USAGE); + + if (mdb_walk("zone", (mdb_walk_cb_t)zdid_lookup_cb, &addr) == -1) { + mdb_warn("failed to walk zone"); + return (DCMD_ERR); + } + + return (DCMD_OK); +} + int zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -96,10 +147,10 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) */ if (DCMD_HDRSPEC(flags)) { if (ropt_given == FALSE) - mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n", + mdb_printf("%<u>%?s %4s %-13s %-19s %-s%</u>\n", "ADDR", "ID", "STATUS", "NAME", "PATH"); else - mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n", + mdb_printf("%<u>%?s %6s %10s %10s %-19s%</u>\n", "ADDR", "ID", "REFS", "CREFS", "NAME"); } @@ -138,7 +189,7 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) statusp = zone_status_names[zn.zone_status]; else statusp = "???"; - mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id, + mdb_printf("%0?p %4d %-13s %-19s %s\n", addr, zn.zone_id, statusp, name, path); } else { /* @@ -146,7 +197,7 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * Display the zone's subsystem-specific reference counts if * the user specified the '-v' option. */ - mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id, + mdb_printf("%0?p %6d %10u %10u %-19s\n", addr, zn.zone_id, zn.zone_ref, zn.zone_cred_ref, name); if (vopt_given == TRUE) { GElf_Sym subsys_names_sym; @@ -384,7 +435,7 @@ zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * Prepare to output the specified zone's ZSD information. */ if (DCMD_HDRSPEC(flags)) - mdb_printf("%<u>%-20s %?s %?s %8s%</u>\n", "ZONE", "KEY", + mdb_printf("%<u>%-19s %?s %?s %8s%</u>\n", "ZONE", "KEY", "VALUE", "FLAGS"); len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name); if (len > 0) { @@ -393,7 +444,7 @@ zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } else { (void) strcpy(name, "??"); } - mdb_printf("%-20s ", name); + mdb_printf("%-19s ", name); /* * Display the requested ZSD entries. diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.h b/usr/src/cmd/mdb/common/modules/genunix/zone.h index e0e5038527..94a383e41c 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/zone.h +++ b/usr/src/cmd/mdb/common/modules/genunix/zone.h @@ -27,14 +27,14 @@ #ifndef _ZONE_H #define _ZONE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_modapi.h> #ifdef __cplusplus extern "C" { #endif +extern int zid2zone(uintptr_t, uint_t, int argc, const mdb_arg_t *); +extern int zdid2zone(uintptr_t, uint_t, int argc, const mdb_arg_t *); extern int zoneprt(uintptr_t, uint_t, int argc, const mdb_arg_t *); extern int zone_walk_init(mdb_walk_state_t *); diff --git a/usr/src/cmd/mdb/common/modules/libc/libc.c b/usr/src/cmd/mdb/common/modules/libc/libc.c index 967198e40b..c4b713f096 100644 --- a/usr/src/cmd/mdb/common/modules/libc/libc.c +++ b/usr/src/cmd/mdb/common/modules/libc/libc.c @@ -137,6 +137,8 @@ d_ucontext(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) uc.uc_stack.ss_sp, uc.uc_stack.ss_size, stack_flags(&uc.uc_stack)); mdb_printf(" mcontext = 0x%p\n", addr + OFFSETOF(ucontext_t, uc_mcontext)); + mdb_printf(" brand = 0x%p 0x%p 0x%p\n", + uc.uc_brand_data[0], uc.uc_brand_data[1], uc.uc_brand_data[2]); return (DCMD_OK); } @@ -841,14 +843,19 @@ d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) prt_addr(uberdata.all_lwps, 1), prt_addr(uberdata.all_zombies, 0)); - HD("nthreads nzombies ndaemons pid sigacthandler"); - mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n", + HD("nthreads nzombies ndaemons pid"); + mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d\n", OFFSET(nthreads), uberdata.nthreads, uberdata.nzombies, uberdata.ndaemons, - (int)uberdata.pid, - prt_addr((void *)uberdata.sigacthandler, 0)); + (int)uberdata.pid); + + HD("sigacthandler setctxt"); + mdb_printf(OFFSTR "%s %s\n", + OFFSET(sigacthandler), + prt_addr((void *)uberdata.sigacthandler, 1), + prt_addr((void *)uberdata.setctxt, 1)); HD("lwp_stacks lwp_laststack nfreestack stk_cache"); mdb_printf(OFFSTR "%s %s %-10d %d\n", @@ -871,12 +878,17 @@ d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) prt_addr(uberdata.ulwp_replace_last, 1), prt_addr(uberdata.atforklist, 0)); - HD("robustlocks robustlist progname"); - mdb_printf(OFFSTR "%s %s %s\n", + HD("robustlocks robustlist"); + mdb_printf(OFFSTR "%s %s\n", OFFSET(robustlocks), prt_addr(uberdata.robustlocks, 1), - prt_addr(uberdata.robustlist, 1), - prt_addr(uberdata.progname, 0)); + prt_addr(uberdata.robustlist, 1)); + + HD("progname ub_broot"); + mdb_printf(OFFSTR "%s %s\n", + OFFSET(progname), + prt_addr(uberdata.progname, 1), + prt_addr(uberdata.ub_broot, 1)); HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail"); mdb_printf(OFFSTR "%s %s %-10d %d\n", diff --git a/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c b/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c index c39583b1f5..6556fc9cd5 100644 --- a/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c +++ b/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c @@ -24,7 +24,7 @@ */ /* - * Copyright 2014 Joyent, Inc. All rights reserved. + * Copyright 2015 Joyent, Inc. All rights reserved. * Copyright (c) 2014, Tegile Systems Inc. All rights reserved. */ @@ -34,6 +34,7 @@ #include <sys/sunmdi.h> #include <sys/list.h> #include <sys/scsi/scsi.h> +#include <sys/refhash.h> #pragma pack(1) #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_type.h> @@ -47,7 +48,6 @@ #pragma pack() #include <sys/scsi/adapters/mpt_sas/mptsas_var.h> -#include <sys/scsi/adapters/mpt_sas/mptsas_hash.h> struct { int value; diff --git a/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c new file mode 100644 index 0000000000..6609097742 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/v8/mdb_v8.c @@ -0,0 +1,5709 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + +/* + * mdb(1M) module for debugging the V8 JavaScript engine. This implementation + * makes heavy use of metadata defined in the V8 binary for inspecting in-memory + * structures. Canned configurations can be manually loaded for V8 binaries + * that predate this metadata. See mdb_v8_cfg.c for details. + * + * NOTE: This dmod implementation (including this file and related headers and C + * files) exist in both the Node and illumos source trees. THESE SHOULD BE KEPT + * IN SYNC. The version in the Node tree is built directly into modern Node + * binaries as part of the build process, and the version in the illumos source + * tree is delivered with the OS for debugging Node binaries that predate + * support for including the dmod directly in the binary. Note too that these + * files have different licenses to match their corresponding repositories. + */ + +/* + * We hard-code our MDB_API_VERSION to be 3 to allow this module to be + * compiled on systems with higher version numbers, but still allow the + * resulting binary object to be used on older systems. (We do not make use + * of functionality present in versions later than 3.) This is particularly + * important for mdb_v8 because (1) it's used in particular to debug + * application-level software and (2) it has a history of rapid evolution. + */ +#define MDB_API_VERSION 3 + +#include <sys/mdb_modapi.h> +#include <assert.h> +#include <ctype.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <libproc.h> +#include <sys/avl.h> +#include <alloca.h> + +#include "v8dbg.h" +#include "v8cfg.h" + +#define offsetof(s, m) ((size_t)(&(((s *)0)->m))) + +/* + * The "v8_class" and "v8_field" structures describe the C++ classes used to + * represent V8 heap objects. + */ +typedef struct v8_class { + struct v8_class *v8c_next; /* list linkage */ + struct v8_class *v8c_parent; /* parent class (inheritance) */ + struct v8_field *v8c_fields; /* array of class fields */ + size_t v8c_start; /* offset of first class field */ + size_t v8c_end; /* offset of first subclass field */ + char v8c_name[64]; /* heap object class name */ +} v8_class_t; + +typedef struct v8_field { + struct v8_field *v8f_next; /* list linkage */ + ssize_t v8f_offset; /* field offset */ + char v8f_name[64]; /* field name */ + boolean_t v8f_isbyte; /* 1-byte int field */ + boolean_t v8f_isstr; /* NUL-terminated string */ +} v8_field_t; + +/* + * Similarly, the "v8_enum" structure describes an enum from V8. + */ +typedef struct { + char v8e_name[64]; + uint_t v8e_value; +} v8_enum_t; + +/* + * During configuration, the dmod updates these globals with the actual set of + * classes, types, and frame types based on the debug metadata. + */ +static v8_class_t *v8_classes; + +static v8_enum_t v8_types[128]; +static int v8_next_type; + +static v8_enum_t v8_frametypes[16]; +static int v8_next_frametype; + +static int v8_warnings; +static int v8_silent; + +/* + * The following constants describe offsets from the frame pointer that are used + * to inspect each stack frame. They're initialized from the debug metadata. + */ +static ssize_t V8_OFF_FP_CONTEXT; +static ssize_t V8_OFF_FP_MARKER; +static ssize_t V8_OFF_FP_FUNCTION; +static ssize_t V8_OFF_FP_ARGS; + +/* + * The following constants are used by macros defined in heap-dbg-common.h to + * examine the types of various V8 heap objects. In general, the macros should + * be preferred to using the constants directly. The values of these constants + * are initialized from the debug metadata. + */ +static intptr_t V8_FirstNonstringType; +static intptr_t V8_IsNotStringMask; +static intptr_t V8_StringTag; +static intptr_t V8_NotStringTag; +static intptr_t V8_StringEncodingMask; +static intptr_t V8_TwoByteStringTag; +static intptr_t V8_AsciiStringTag; +static intptr_t V8_StringRepresentationMask; +static intptr_t V8_SeqStringTag; +static intptr_t V8_ConsStringTag; +static intptr_t V8_SlicedStringTag; +static intptr_t V8_ExternalStringTag; +static intptr_t V8_FailureTag; +static intptr_t V8_FailureTagMask; +static intptr_t V8_HeapObjectTag; +static intptr_t V8_HeapObjectTagMask; +static intptr_t V8_SmiTag; +static intptr_t V8_SmiTagMask; +static intptr_t V8_SmiValueShift; +static intptr_t V8_SmiShiftSize; +static intptr_t V8_PointerSizeLog2; + +static intptr_t V8_ISSHARED_SHIFT; +static intptr_t V8_DICT_SHIFT; +static intptr_t V8_DICT_PREFIX_SIZE; +static intptr_t V8_DICT_ENTRY_SIZE; +static intptr_t V8_DICT_START_INDEX; +static intptr_t V8_FIELDINDEX_MASK; +static intptr_t V8_FIELDINDEX_SHIFT; +static intptr_t V8_PROP_IDX_CONTENT; +static intptr_t V8_PROP_IDX_FIRST; +static intptr_t V8_PROP_TYPE_FIELD; +static intptr_t V8_PROP_TYPE_MASK; +static intptr_t V8_PROP_DESC_KEY; +static intptr_t V8_PROP_DESC_DETAILS; +static intptr_t V8_PROP_DESC_VALUE; +static intptr_t V8_PROP_DESC_SIZE; +static intptr_t V8_TRANSITIONS_IDX_DESC; + +static intptr_t V8_TYPE_JSOBJECT = -1; +static intptr_t V8_TYPE_JSARRAY = -1; +static intptr_t V8_TYPE_JSFUNCTION = -1; +static intptr_t V8_TYPE_FIXEDARRAY = -1; + +static intptr_t V8_ELEMENTS_KIND_SHIFT; +static intptr_t V8_ELEMENTS_KIND_BITCOUNT; +static intptr_t V8_ELEMENTS_FAST_ELEMENTS; +static intptr_t V8_ELEMENTS_FAST_HOLEY_ELEMENTS; +static intptr_t V8_ELEMENTS_DICTIONARY_ELEMENTS; + +/* + * Although we have this information in v8_classes, the following offsets are + * defined explicitly because they're used directly in code below. + */ +static ssize_t V8_OFF_CODE_INSTRUCTION_SIZE; +static ssize_t V8_OFF_CODE_INSTRUCTION_START; +static ssize_t V8_OFF_CONSSTRING_FIRST; +static ssize_t V8_OFF_CONSSTRING_SECOND; +static ssize_t V8_OFF_EXTERNALSTRING_RESOURCE; +static ssize_t V8_OFF_FIXEDARRAY_DATA; +static ssize_t V8_OFF_FIXEDARRAY_LENGTH; +static ssize_t V8_OFF_HEAPNUMBER_VALUE; +static ssize_t V8_OFF_HEAPOBJECT_MAP; +static ssize_t V8_OFF_JSARRAY_LENGTH; +static ssize_t V8_OFF_JSDATE_VALUE; +static ssize_t V8_OFF_JSFUNCTION_SHARED; +static ssize_t V8_OFF_JSOBJECT_ELEMENTS; +static ssize_t V8_OFF_JSOBJECT_PROPERTIES; +static ssize_t V8_OFF_MAP_CONSTRUCTOR; +static ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES; +static ssize_t V8_OFF_MAP_INSTANCE_ATTRIBUTES; +static ssize_t V8_OFF_MAP_INSTANCE_DESCRIPTORS; +static ssize_t V8_OFF_MAP_INSTANCE_SIZE; +static ssize_t V8_OFF_MAP_BIT_FIELD; +static ssize_t V8_OFF_MAP_BIT_FIELD2; +static ssize_t V8_OFF_MAP_BIT_FIELD3; +static ssize_t V8_OFF_MAP_TRANSITIONS; +static ssize_t V8_OFF_ODDBALL_TO_STRING; +static ssize_t V8_OFF_SCRIPT_LINE_ENDS; +static ssize_t V8_OFF_SCRIPT_NAME; +static ssize_t V8_OFF_SCRIPT_SOURCE; +static ssize_t V8_OFF_SEQASCIISTR_CHARS; +static ssize_t V8_OFF_SEQONEBYTESTR_CHARS; +static ssize_t V8_OFF_SEQTWOBYTESTR_CHARS; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_CODE; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_END_POSITION; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_LENGTH; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_SCRIPT; +static ssize_t V8_OFF_SHAREDFUNCTIONINFO_NAME; +static ssize_t V8_OFF_SLICEDSTRING_PARENT; +static ssize_t V8_OFF_SLICEDSTRING_OFFSET; +static ssize_t V8_OFF_STRING_LENGTH; + +/* see node_string.h */ +#define NODE_OFF_EXTSTR_DATA sizeof (uintptr_t) + +#define V8_CONSTANT_OPTIONAL 1 +#define V8_CONSTANT_HASFALLBACK 2 +#define V8_CONSTANT_REMOVED 4 + +#define V8_CONSTANT_MAJORSHIFT 3 +#define V8_CONSTANT_MAJORMASK ((1 << 4) - 1) +#define V8_CONSTANT_MAJOR(flags) \ + (((flags) >> V8_CONSTANT_MAJORSHIFT) & V8_CONSTANT_MAJORMASK) + +#define V8_CONSTANT_MINORSHIFT 7 +#define V8_CONSTANT_MINORMASK ((1 << 9) - 1) +#define V8_CONSTANT_MINOR(flags) \ + (((flags) >> V8_CONSTANT_MINORSHIFT) & V8_CONSTANT_MINORMASK) + +#define V8_CONSTANT_FALLBACK(maj, min) \ + (V8_CONSTANT_OPTIONAL | V8_CONSTANT_HASFALLBACK | \ + ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT)) + +#define V8_CONSTANT_REMOVED_SINCE(maj, min) \ + (V8_CONSTANT_REMOVED | \ + ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT)) + +/* + * Table of constants used directly by this file. + */ +typedef struct v8_constant { + intptr_t *v8c_valp; + const char *v8c_symbol; + uint32_t v8c_flags; + intptr_t v8c_fallback; +} v8_constant_t; + +static v8_constant_t v8_constants[] = { + { &V8_OFF_FP_CONTEXT, "v8dbg_off_fp_context" }, + { &V8_OFF_FP_FUNCTION, "v8dbg_off_fp_function" }, + { &V8_OFF_FP_MARKER, "v8dbg_off_fp_marker" }, + { &V8_OFF_FP_ARGS, "v8dbg_off_fp_args" }, + + { &V8_FirstNonstringType, "v8dbg_FirstNonstringType" }, + { &V8_IsNotStringMask, "v8dbg_IsNotStringMask" }, + { &V8_StringTag, "v8dbg_StringTag" }, + { &V8_NotStringTag, "v8dbg_NotStringTag" }, + { &V8_StringEncodingMask, "v8dbg_StringEncodingMask" }, + { &V8_TwoByteStringTag, "v8dbg_TwoByteStringTag" }, + { &V8_AsciiStringTag, "v8dbg_AsciiStringTag" }, + { &V8_StringRepresentationMask, "v8dbg_StringRepresentationMask" }, + { &V8_SeqStringTag, "v8dbg_SeqStringTag" }, + { &V8_ConsStringTag, "v8dbg_ConsStringTag" }, + { &V8_SlicedStringTag, "v8dbg_SlicedStringTag", + V8_CONSTANT_FALLBACK(0, 0), 0x3 }, + { &V8_ExternalStringTag, "v8dbg_ExternalStringTag" }, + { &V8_FailureTag, "v8dbg_FailureTag", + V8_CONSTANT_REMOVED_SINCE(3, 28) }, + { &V8_FailureTagMask, "v8dbg_FailureTagMask", + V8_CONSTANT_REMOVED_SINCE(3, 28) }, + { &V8_HeapObjectTag, "v8dbg_HeapObjectTag" }, + { &V8_HeapObjectTagMask, "v8dbg_HeapObjectTagMask" }, + { &V8_SmiTag, "v8dbg_SmiTag" }, + { &V8_SmiTagMask, "v8dbg_SmiTagMask" }, + { &V8_SmiValueShift, "v8dbg_SmiValueShift" }, + { &V8_SmiShiftSize, "v8dbg_SmiShiftSize", +#ifdef _LP64 + V8_CONSTANT_FALLBACK(0, 0), 31 }, +#else + V8_CONSTANT_FALLBACK(0, 0), 0 }, +#endif + { &V8_PointerSizeLog2, "v8dbg_PointerSizeLog2" }, + + { &V8_DICT_SHIFT, "v8dbg_bit_field3_dictionary_map_shift", + V8_CONSTANT_FALLBACK(3, 13), 24 }, + { &V8_DICT_PREFIX_SIZE, "v8dbg_dict_prefix_size", + V8_CONSTANT_FALLBACK(3, 11), 2 }, + { &V8_DICT_ENTRY_SIZE, "v8dbg_dict_entry_size", + V8_CONSTANT_FALLBACK(3, 11), 3 }, + { &V8_DICT_START_INDEX, "v8dbg_dict_start_index", + V8_CONSTANT_FALLBACK(3, 11), 3 }, + { &V8_FIELDINDEX_MASK, "v8dbg_fieldindex_mask", + V8_CONSTANT_FALLBACK(3, 26), 0x3ff00000 }, + { &V8_FIELDINDEX_SHIFT, "v8dbg_fieldindex_shift", + V8_CONSTANT_FALLBACK(3, 26), 20 }, + { &V8_ISSHARED_SHIFT, "v8dbg_isshared_shift", + V8_CONSTANT_FALLBACK(3, 11), 0 }, + { &V8_PROP_IDX_FIRST, "v8dbg_prop_idx_first" }, + { &V8_PROP_TYPE_FIELD, "v8dbg_prop_type_field" }, + { &V8_PROP_TYPE_MASK, "v8dbg_prop_type_mask" }, + { &V8_PROP_IDX_CONTENT, "v8dbg_prop_idx_content", + V8_CONSTANT_OPTIONAL }, + { &V8_PROP_DESC_KEY, "v8dbg_prop_desc_key", + V8_CONSTANT_FALLBACK(0, 0), 0 }, + { &V8_PROP_DESC_DETAILS, "v8dbg_prop_desc_details", + V8_CONSTANT_FALLBACK(0, 0), 1 }, + { &V8_PROP_DESC_VALUE, "v8dbg_prop_desc_value", + V8_CONSTANT_FALLBACK(0, 0), 2 }, + { &V8_PROP_DESC_SIZE, "v8dbg_prop_desc_size", + V8_CONSTANT_FALLBACK(0, 0), 3 }, + { &V8_TRANSITIONS_IDX_DESC, "v8dbg_transitions_idx_descriptors", + V8_CONSTANT_OPTIONAL }, + + { &V8_ELEMENTS_KIND_SHIFT, "v8dbg_elements_kind_shift", + V8_CONSTANT_FALLBACK(0, 0), 3 }, + { &V8_ELEMENTS_KIND_BITCOUNT, "v8dbg_elements_kind_bitcount", + V8_CONSTANT_FALLBACK(0, 0), 5 }, + { &V8_ELEMENTS_FAST_ELEMENTS, + "v8dbg_elements_fast_elements", + V8_CONSTANT_FALLBACK(0, 0), 2 }, + { &V8_ELEMENTS_FAST_HOLEY_ELEMENTS, + "v8dbg_elements_fast_holey_elements", + V8_CONSTANT_FALLBACK(0, 0), 3 }, + { &V8_ELEMENTS_DICTIONARY_ELEMENTS, + "v8dbg_elements_dictionary_elements", + V8_CONSTANT_FALLBACK(0, 0), 6 }, +}; + +static int v8_nconstants = sizeof (v8_constants) / sizeof (v8_constants[0]); + +typedef struct v8_offset { + ssize_t *v8o_valp; + const char *v8o_class; + const char *v8o_member; + boolean_t v8o_optional; +} v8_offset_t; + +static v8_offset_t v8_offsets[] = { + { &V8_OFF_CODE_INSTRUCTION_SIZE, + "Code", "instruction_size" }, + { &V8_OFF_CODE_INSTRUCTION_START, + "Code", "instruction_start" }, + { &V8_OFF_CONSSTRING_FIRST, + "ConsString", "first" }, + { &V8_OFF_CONSSTRING_SECOND, + "ConsString", "second" }, + { &V8_OFF_EXTERNALSTRING_RESOURCE, + "ExternalString", "resource" }, + { &V8_OFF_FIXEDARRAY_DATA, + "FixedArray", "data" }, + { &V8_OFF_FIXEDARRAY_LENGTH, + "FixedArray", "length" }, + { &V8_OFF_HEAPNUMBER_VALUE, + "HeapNumber", "value" }, + { &V8_OFF_HEAPOBJECT_MAP, + "HeapObject", "map" }, + { &V8_OFF_JSARRAY_LENGTH, + "JSArray", "length" }, + { &V8_OFF_JSDATE_VALUE, + "JSDate", "value", B_TRUE }, + { &V8_OFF_JSFUNCTION_SHARED, + "JSFunction", "shared" }, + { &V8_OFF_JSOBJECT_ELEMENTS, + "JSObject", "elements" }, + { &V8_OFF_JSOBJECT_PROPERTIES, + "JSObject", "properties" }, + { &V8_OFF_MAP_CONSTRUCTOR, + "Map", "constructor" }, + { &V8_OFF_MAP_INOBJECT_PROPERTIES, + "Map", "inobject_properties" }, + { &V8_OFF_MAP_INSTANCE_ATTRIBUTES, + "Map", "instance_attributes" }, + { &V8_OFF_MAP_INSTANCE_DESCRIPTORS, + "Map", "instance_descriptors", B_TRUE }, + { &V8_OFF_MAP_TRANSITIONS, + "Map", "transitions", B_TRUE }, + { &V8_OFF_MAP_INSTANCE_SIZE, + "Map", "instance_size" }, + { &V8_OFF_MAP_BIT_FIELD2, + "Map", "bit_field2", B_TRUE }, + { &V8_OFF_MAP_BIT_FIELD3, + "Map", "bit_field3", B_TRUE }, + { &V8_OFF_ODDBALL_TO_STRING, + "Oddball", "to_string" }, + { &V8_OFF_SCRIPT_LINE_ENDS, + "Script", "line_ends" }, + { &V8_OFF_SCRIPT_NAME, + "Script", "name" }, + { &V8_OFF_SCRIPT_SOURCE, + "Script", "source" }, + { &V8_OFF_SEQASCIISTR_CHARS, + "SeqAsciiString", "chars", B_TRUE }, + { &V8_OFF_SEQONEBYTESTR_CHARS, + "SeqOneByteString", "chars", B_TRUE }, + { &V8_OFF_SEQTWOBYTESTR_CHARS, + "SeqTwoByteString", "chars", B_TRUE }, + { &V8_OFF_SHAREDFUNCTIONINFO_CODE, + "SharedFunctionInfo", "code" }, + { &V8_OFF_SHAREDFUNCTIONINFO_END_POSITION, + "SharedFunctionInfo", "end_position" }, + { &V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION, + "SharedFunctionInfo", "function_token_position" }, + { &V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME, + "SharedFunctionInfo", "inferred_name" }, + { &V8_OFF_SHAREDFUNCTIONINFO_LENGTH, + "SharedFunctionInfo", "length" }, + { &V8_OFF_SHAREDFUNCTIONINFO_NAME, + "SharedFunctionInfo", "name" }, + { &V8_OFF_SHAREDFUNCTIONINFO_SCRIPT, + "SharedFunctionInfo", "script" }, + { &V8_OFF_SLICEDSTRING_OFFSET, + "SlicedString", "offset" }, + { &V8_OFF_SLICEDSTRING_PARENT, + "SlicedString", "parent", B_TRUE }, + { &V8_OFF_STRING_LENGTH, + "String", "length" }, +}; + +static int v8_noffsets = sizeof (v8_offsets) / sizeof (v8_offsets[0]); + +static uintptr_t v8_major; +static uintptr_t v8_minor; +static uintptr_t v8_build; +static uintptr_t v8_patch; + +static int autoconf_iter_symbol(mdb_symbol_t *, void *); +static v8_class_t *conf_class_findcreate(const char *); +static v8_field_t *conf_field_create(v8_class_t *, const char *, size_t); +static char *conf_next_part(char *, char *); +static int conf_update_parent(const char *); +static int conf_update_field(v8_cfg_t *, const char *); +static int conf_update_enum(v8_cfg_t *, const char *, const char *, + v8_enum_t *); +static int conf_update_type(v8_cfg_t *, const char *); +static int conf_update_frametype(v8_cfg_t *, const char *); +static void conf_class_compute_offsets(v8_class_t *); + +static int read_typebyte(uint8_t *, uintptr_t); +static int heap_offset(const char *, const char *, ssize_t *); +static int jsfunc_name(uintptr_t, char **, size_t *); + +/* + * When iterating properties, it's useful to keep track of what kinds of + * properties were found. This is useful for developers to identify objects of + * different kinds in order to debug them. + */ +typedef enum { + JPI_NONE = 0, + + /* + * Indicates how properties are stored in this object. There can be + * both numeric properties and some of the other kinds. + */ + JPI_NUMERIC = 0x01, /* numeric-named properties in "elements" */ + JPI_DICT = 0x02, /* dictionary properties */ + JPI_INOBJECT = 0x04, /* properties stored inside object */ + JPI_PROPS = 0x08, /* "properties" array */ + + /* error-like cases */ + JPI_SKIPPED = 0x10, /* some properties were skipped */ + JPI_BADLAYOUT = 0x20, /* we didn't recognize the layout at all */ + + /* fallback cases */ + JPI_HASTRANSITIONS = 0x100, /* found a transitions array */ + JPI_HASCONTENT = 0x200, /* found a separate content array */ +} jspropinfo_t; + +typedef struct jsobj_print { + char **jsop_bufp; + size_t *jsop_lenp; + int jsop_indent; + uint64_t jsop_depth; + boolean_t jsop_printaddr; + uintptr_t jsop_baseaddr; + int jsop_nprops; + const char *jsop_member; + boolean_t jsop_found; + boolean_t jsop_descended; + jspropinfo_t jsop_propinfo; +} jsobj_print_t; + +static int jsobj_print_number(uintptr_t, jsobj_print_t *); +static int jsobj_print_oddball(uintptr_t, jsobj_print_t *); +static int jsobj_print_jsobject(uintptr_t, jsobj_print_t *); +static int jsobj_print_jsarray(uintptr_t, jsobj_print_t *); +static int jsobj_print_jsfunction(uintptr_t, jsobj_print_t *); +static int jsobj_print_jsdate(uintptr_t, jsobj_print_t *); + +/* + * Returns 1 if the V8 version v8_major.v8.minor is strictly older than + * the V8 version represented by "flags". + * Returns 0 otherwise. + */ +static int +v8_version_older(uintptr_t v8_major, uintptr_t v8_minor, uint32_t flags) { + return (v8_major < V8_CONSTANT_MAJOR(flags) || + (v8_major == V8_CONSTANT_MAJOR(flags) && + v8_minor < V8_CONSTANT_MINOR(flags))); +} + +/* + * Invoked when this dmod is initially loaded to load the set of classes, enums, + * and other constants from the metadata in the target binary. + */ +static int +autoconfigure(v8_cfg_t *cfgp) +{ + v8_class_t *clp; + v8_enum_t *ep; + struct v8_constant *cnp; + int ii; + int failed = 0; + + assert(v8_classes == NULL); + + /* + * Iterate all global symbols looking for metadata. + */ + if (cfgp->v8cfg_iter(cfgp, autoconf_iter_symbol, cfgp) != 0) { + mdb_warn("failed to autoconfigure V8 support\n"); + return (-1); + } + + /* + * By now we've configured all of the classes so we can update the + * "start" and "end" fields in each class with information from its + * parent class. + */ + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) { + if (clp->v8c_end != (size_t)-1) + continue; + + conf_class_compute_offsets(clp); + }; + + /* + * Load various constants used directly in the module. + */ + for (ii = 0; ii < v8_nconstants; ii++) { + cnp = &v8_constants[ii]; + + if (cfgp->v8cfg_readsym(cfgp, + cnp->v8c_symbol, cnp->v8c_valp) != -1) { + continue; + } + + if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL) && + (!(cnp->v8c_flags & V8_CONSTANT_REMOVED) || + v8_version_older(v8_major, v8_minor, cnp->v8c_flags))) { + mdb_warn("failed to read \"%s\"", cnp->v8c_symbol); + failed++; + continue; + } + + if (!(cnp->v8c_flags & V8_CONSTANT_HASFALLBACK) || + v8_major < V8_CONSTANT_MAJOR(cnp->v8c_flags) || + (v8_major == V8_CONSTANT_MAJOR(cnp->v8c_flags) && + v8_minor < V8_CONSTANT_MINOR(cnp->v8c_flags))) { + *cnp->v8c_valp = -1; + continue; + } + + /* + * We have a fallback -- and we know that the version satisfies + * the fallback's version constraints; use the fallback value. + */ + *cnp->v8c_valp = cnp->v8c_fallback; + } + + /* + * Load type values for well-known classes that we use a lot. + */ + for (ep = v8_types; ep->v8e_name[0] != '\0'; ep++) { + if (strcmp(ep->v8e_name, "JSObject") == 0) + V8_TYPE_JSOBJECT = ep->v8e_value; + + if (strcmp(ep->v8e_name, "JSArray") == 0) + V8_TYPE_JSARRAY = ep->v8e_value; + + if (strcmp(ep->v8e_name, "JSFunction") == 0) + V8_TYPE_JSFUNCTION = ep->v8e_value; + + if (strcmp(ep->v8e_name, "FixedArray") == 0) + V8_TYPE_FIXEDARRAY = ep->v8e_value; + } + + if (V8_TYPE_JSOBJECT == -1) { + mdb_warn("couldn't find JSObject type\n"); + failed++; + } + + if (V8_TYPE_JSARRAY == -1) { + mdb_warn("couldn't find JSArray type\n"); + failed++; + } + + if (V8_TYPE_JSFUNCTION == -1) { + mdb_warn("couldn't find JSFunction type\n"); + failed++; + } + + if (V8_TYPE_FIXEDARRAY == -1) { + mdb_warn("couldn't find FixedArray type\n"); + failed++; + } + + /* + * Finally, load various class offsets. + */ + for (ii = 0; ii < v8_noffsets; ii++) { + struct v8_offset *offp = &v8_offsets[ii]; + const char *klass = offp->v8o_class; + +again: + if (heap_offset(klass, offp->v8o_member, offp->v8o_valp) == 0) + continue; + + if (strcmp(klass, "FixedArray") == 0) { + /* + * The V8 included in node v0.6 uses a FixedArrayBase + * class to contain the "length" field, while the one + * in v0.4 has no such base class and stores the field + * directly in FixedArray; if we failed to derive + * the offset from FixedArray, try FixedArrayBase. + */ + klass = "FixedArrayBase"; + goto again; + } + + if (offp->v8o_optional) { + *offp->v8o_valp = -1; + continue; + } + + mdb_warn("couldn't find class \"%s\", field \"%s\"\n", + offp->v8o_class, offp->v8o_member); + failed++; + } + + if (!((V8_OFF_SEQASCIISTR_CHARS != -1) ^ + (V8_OFF_SEQONEBYTESTR_CHARS != -1))) { + mdb_warn("expected exactly one of SeqAsciiString and " + "SeqOneByteString to be defined\n"); + failed++; + } + + if (V8_OFF_SEQONEBYTESTR_CHARS != -1) + V8_OFF_SEQASCIISTR_CHARS = V8_OFF_SEQONEBYTESTR_CHARS; + + if (V8_OFF_SEQTWOBYTESTR_CHARS == -1) + V8_OFF_SEQTWOBYTESTR_CHARS = V8_OFF_SEQASCIISTR_CHARS; + + if (V8_OFF_SLICEDSTRING_PARENT == -1) + V8_OFF_SLICEDSTRING_PARENT = V8_OFF_SLICEDSTRING_OFFSET - + sizeof (uintptr_t); + + /* + * If we don't have bit_field/bit_field2 for Map, we know that they're + * the second and third byte of instance_attributes. + */ + if (V8_OFF_MAP_BIT_FIELD == -1) + V8_OFF_MAP_BIT_FIELD = V8_OFF_MAP_INSTANCE_ATTRIBUTES + 2; + + if (V8_OFF_MAP_BIT_FIELD2 == -1) + V8_OFF_MAP_BIT_FIELD2 = V8_OFF_MAP_INSTANCE_ATTRIBUTES + 3; + + return (failed ? -1 : 0); +} + +/* ARGSUSED */ +static int +autoconf_iter_symbol(mdb_symbol_t *symp, void *arg) +{ + v8_cfg_t *cfgp = arg; + + if (strncmp(symp->sym_name, "v8dbg_parent_", + sizeof ("v8dbg_parent_") - 1) == 0) + return (conf_update_parent(symp->sym_name)); + + if (strncmp(symp->sym_name, "v8dbg_class_", + sizeof ("v8dbg_class_") - 1) == 0) + return (conf_update_field(cfgp, symp->sym_name)); + + if (strncmp(symp->sym_name, "v8dbg_type_", + sizeof ("v8dbg_type_") - 1) == 0) + return (conf_update_type(cfgp, symp->sym_name)); + + if (strncmp(symp->sym_name, "v8dbg_frametype_", + sizeof ("v8dbg_frametype_") - 1) == 0) + return (conf_update_frametype(cfgp, symp->sym_name)); + + return (0); +} + +/* + * Extracts the next field of a string whose fields are separated by "__" (as + * the V8 metadata symbols are). + */ +static char * +conf_next_part(char *buf, char *start) +{ + char *pp; + + if ((pp = strstr(start, "__")) == NULL) { + mdb_warn("malformed symbol name: %s\n", buf); + return (NULL); + } + + *pp = '\0'; + return (pp + sizeof ("__") - 1); +} + +static v8_class_t * +conf_class_findcreate(const char *name) +{ + v8_class_t *clp, *iclp, *prev = NULL; + int cmp; + + for (iclp = v8_classes; iclp != NULL; iclp = iclp->v8c_next) { + if ((cmp = strcmp(iclp->v8c_name, name)) == 0) + return (iclp); + + if (cmp > 0) + break; + + prev = iclp; + } + + if ((clp = mdb_zalloc(sizeof (*clp), UM_NOSLEEP)) == NULL) + return (NULL); + + (void) strlcpy(clp->v8c_name, name, sizeof (clp->v8c_name)); + clp->v8c_end = (size_t)-1; + clp->v8c_next = iclp; + + if (prev != NULL) { + prev->v8c_next = clp; + } else { + v8_classes = clp; + } + + return (clp); +} + +static v8_field_t * +conf_field_create(v8_class_t *clp, const char *name, size_t offset) +{ + v8_field_t *flp, *iflp; + + if ((flp = mdb_zalloc(sizeof (*flp), UM_NOSLEEP)) == NULL) + return (NULL); + + (void) strlcpy(flp->v8f_name, name, sizeof (flp->v8f_name)); + flp->v8f_offset = offset; + + if (clp->v8c_fields == NULL || clp->v8c_fields->v8f_offset > offset) { + flp->v8f_next = clp->v8c_fields; + clp->v8c_fields = flp; + return (flp); + } + + for (iflp = clp->v8c_fields; iflp->v8f_next != NULL; + iflp = iflp->v8f_next) { + if (iflp->v8f_next->v8f_offset > offset) + break; + } + + flp->v8f_next = iflp->v8f_next; + iflp->v8f_next = flp; + return (flp); +} + +/* + * Given a "v8dbg_parent_X__Y", symbol, update the parent of class X to class Y. + * Note that neither class necessarily exists already. + */ +static int +conf_update_parent(const char *symbol) +{ + char *pp, *qq; + char buf[128]; + v8_class_t *clp, *pclp; + + (void) strlcpy(buf, symbol, sizeof (buf)); + pp = buf + sizeof ("v8dbg_parent_") - 1; + qq = conf_next_part(buf, pp); + + if (qq == NULL) + return (-1); + + clp = conf_class_findcreate(pp); + pclp = conf_class_findcreate(qq); + + if (clp == NULL || pclp == NULL) { + mdb_warn("mdb_v8: out of memory\n"); + return (-1); + } + + clp->v8c_parent = pclp; + return (0); +} + +/* + * Given a "v8dbg_class_CLASS__FIELD__TYPE", symbol, save field "FIELD" into + * class CLASS with the offset described by the symbol. Note that CLASS does + * not necessarily exist already. + */ +static int +conf_update_field(v8_cfg_t *cfgp, const char *symbol) +{ + v8_class_t *clp; + v8_field_t *flp; + intptr_t offset; + char *pp, *qq, *tt; + char buf[128]; + + (void) strlcpy(buf, symbol, sizeof (buf)); + + pp = buf + sizeof ("v8dbg_class_") - 1; + qq = conf_next_part(buf, pp); + + if (qq == NULL || (tt = conf_next_part(buf, qq)) == NULL) + return (-1); + + if (cfgp->v8cfg_readsym(cfgp, symbol, &offset) == -1) { + mdb_warn("failed to read symbol \"%s\"", symbol); + return (-1); + } + + if ((clp = conf_class_findcreate(pp)) == NULL || + (flp = conf_field_create(clp, qq, (size_t)offset)) == NULL) + return (-1); + + if (strcmp(tt, "int") == 0) + flp->v8f_isbyte = B_TRUE; + + if (strcmp(tt, "char") == 0) + flp->v8f_isstr = B_TRUE; + + return (0); +} + +static int +conf_update_enum(v8_cfg_t *cfgp, const char *symbol, const char *name, + v8_enum_t *enp) +{ + intptr_t value; + + if (cfgp->v8cfg_readsym(cfgp, symbol, &value) == -1) { + mdb_warn("failed to read symbol \"%s\"", symbol); + return (-1); + } + + enp->v8e_value = (int)value; + (void) strlcpy(enp->v8e_name, name, sizeof (enp->v8e_name)); + return (0); +} + +/* + * Given a "v8dbg_type_TYPENAME" constant, save the type name in v8_types. Note + * that this enum has multiple integer values with the same string label. + */ +static int +conf_update_type(v8_cfg_t *cfgp, const char *symbol) +{ + char *klass; + v8_enum_t *enp; + char buf[128]; + + if (v8_next_type > sizeof (v8_types) / sizeof (v8_types[0])) { + mdb_warn("too many V8 types\n"); + return (-1); + } + + (void) strlcpy(buf, symbol, sizeof (buf)); + + klass = buf + sizeof ("v8dbg_type_") - 1; + if (conf_next_part(buf, klass) == NULL) + return (-1); + + enp = &v8_types[v8_next_type++]; + return (conf_update_enum(cfgp, symbol, klass, enp)); +} + +/* + * Given a "v8dbg_frametype_TYPENAME" constant, save the frame type in + * v8_frametypes. + */ +static int +conf_update_frametype(v8_cfg_t *cfgp, const char *symbol) +{ + const char *frametype; + v8_enum_t *enp; + + if (v8_next_frametype > + sizeof (v8_frametypes) / sizeof (v8_frametypes[0])) { + mdb_warn("too many V8 frame types\n"); + return (-1); + } + + enp = &v8_frametypes[v8_next_frametype++]; + frametype = symbol + sizeof ("v8dbg_frametype_") - 1; + return (conf_update_enum(cfgp, symbol, frametype, enp)); +} + +/* + * Now that all classes have been loaded, update the "start" and "end" fields of + * each class based on the values of its parent class. + */ +static void +conf_class_compute_offsets(v8_class_t *clp) +{ + v8_field_t *flp; + + assert(clp->v8c_start == 0); + assert(clp->v8c_end == (size_t)-1); + + if (clp->v8c_parent != NULL) { + if (clp->v8c_parent->v8c_end == (size_t)-1) + conf_class_compute_offsets(clp->v8c_parent); + + clp->v8c_start = clp->v8c_parent->v8c_end; + } + + if (clp->v8c_fields == NULL) { + clp->v8c_end = clp->v8c_start; + return; + } + + for (flp = clp->v8c_fields; flp->v8f_next != NULL; flp = flp->v8f_next) + ; + + if (flp == NULL) + clp->v8c_end = clp->v8c_start; + else + clp->v8c_end = flp->v8f_offset + sizeof (uintptr_t); +} + +/* + * Utility functions + */ +#define JSSTR_NONE 0 +#define JSSTR_NUDE JSSTR_NONE + +#define JSSTR_FLAGSHIFT 16 +#define JSSTR_VERBOSE (0x1 << JSSTR_FLAGSHIFT) +#define JSSTR_QUOTED (0x2 << JSSTR_FLAGSHIFT) +#define JSSTR_ISASCII (0x4 << JSSTR_FLAGSHIFT) + +#define JSSTR_MAXDEPTH 512 +#define JSSTR_DEPTH(f) ((f) & ((1 << JSSTR_FLAGSHIFT) - 1)) +#define JSSTR_BUMPDEPTH(f) ((f) + 1) + +static int jsstr_print(uintptr_t, uint_t, char **, size_t *); +static boolean_t jsobj_is_undefined(uintptr_t addr); +static boolean_t jsobj_is_hole(uintptr_t addr); + +static const char * +enum_lookup_str(v8_enum_t *enums, int val, const char *dflt) +{ + v8_enum_t *ep; + + for (ep = enums; ep->v8e_name[0] != '\0'; ep++) { + if (ep->v8e_value == val) + return (ep->v8e_name); + } + + return (dflt); +} + +static void +enum_print(v8_enum_t *enums) +{ + v8_enum_t *itp; + + for (itp = enums; itp->v8e_name[0] != '\0'; itp++) + mdb_printf("%-30s = 0x%02x\n", itp->v8e_name, itp->v8e_value); +} + +/* + * b[v]snprintf behave like [v]snprintf(3c), except that they update the buffer + * and length arguments based on how much buffer space is used by the operation. + * This makes it much easier to combine multiple calls in sequence without + * worrying about buffer overflow. + */ +static size_t +bvsnprintf(char **bufp, size_t *buflenp, const char *format, va_list alist) +{ + size_t rv, len; + + if (*buflenp == 0) + return (vsnprintf(NULL, 0, format, alist)); + + rv = vsnprintf(*bufp, *buflenp, format, alist); + + len = MIN(rv, *buflenp); + *buflenp -= len; + *bufp += len; + + return (len); +} + +static size_t +bsnprintf(char **bufp, size_t *buflenp, const char *format, ...) +{ + va_list alist; + size_t rv; + + va_start(alist, format); + rv = bvsnprintf(bufp, buflenp, format, alist); + va_end(alist); + + return (rv); +} + +static void +v8_warn(const char *format, ...) +{ + char buf[512]; + va_list alist; + int len; + + if (!v8_warnings || v8_silent) + return; + + va_start(alist, format); + (void) vsnprintf(buf, sizeof (buf), format, alist); + va_end(alist); + + /* + * This is made slightly annoying because we need to effectively + * preserve the original format string to allow for mdb to use the + * new-line at the end to indicate that strerror should be elided. + */ + if ((len = strlen(format)) > 0 && format[len - 1] == '\n') { + buf[strlen(buf) - 1] = '\0'; + mdb_warn("%s\n", buf); + } else { + mdb_warn("%s", buf); + } +} + +static v8_field_t * +conf_field_lookup(const char *klass, const char *field) +{ + v8_class_t *clp; + v8_field_t *flp; + + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) { + if (strcmp(klass, clp->v8c_name) == 0) + break; + } + + if (clp == NULL) + return (NULL); + + for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) { + if (strcmp(field, flp->v8f_name) == 0) + break; + } + + return (flp); +} + +/* + * Returns in "offp" the offset of field "field" in C++ class "klass". + */ +static int +heap_offset(const char *klass, const char *field, ssize_t *offp) +{ + v8_field_t *flp; + + flp = conf_field_lookup(klass, field); + + if (flp == NULL) + return (-1); + + *offp = V8_OFF_HEAP(flp->v8f_offset); + return (0); +} + +/* + * Assuming "addr" is an instance of the C++ heap class "klass", read into *valp + * the pointer-sized value of field "field". + */ +static int +read_heap_ptr(uintptr_t *valp, uintptr_t addr, ssize_t off) +{ + if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) { + v8_warn("failed to read offset %d from %p", off, addr); + return (-1); + } + + return (0); +} + +/* + * Like read_heap_ptr, but assume the field is an SMI and store the actual value + * into *valp rather than the encoded representation. + */ +static int +read_heap_smi(uintptr_t *valp, uintptr_t addr, ssize_t off) +{ + if (read_heap_ptr(valp, addr, off) != 0) + return (-1); + + if (!V8_IS_SMI(*valp)) { + v8_warn("expected SMI, got %p\n", *valp); + return (-1); + } + + *valp = V8_SMI_VALUE(*valp); + + return (0); +} + +static int +read_heap_double(double *valp, uintptr_t addr, ssize_t off) +{ + if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) { + v8_warn("failed to read heap value at %p", addr + off); + return (-1); + } + + return (0); +} + +/* + * Assuming "addr" refers to a FixedArray, return a newly-allocated array + * representing its contents. + */ +static int +read_heap_array(uintptr_t addr, uintptr_t **retp, size_t *lenp, int flags) +{ + uint8_t type; + uintptr_t len; + + if (!V8_IS_HEAPOBJECT(addr)) + return (-1); + + if (read_typebyte(&type, addr) != 0) + return (-1); + + if (type != V8_TYPE_FIXEDARRAY) + return (-1); + + if (read_heap_smi(&len, addr, V8_OFF_FIXEDARRAY_LENGTH) != 0) + return (-1); + + *lenp = len; + + if (len == 0) { + *retp = NULL; + return (0); + } + + if ((*retp = mdb_zalloc(len * sizeof (uintptr_t), flags)) == NULL) + return (-1); + + if (mdb_vread(*retp, len * sizeof (uintptr_t), + addr + V8_OFF_FIXEDARRAY_DATA) == -1) { + if (!(flags & UM_GC)) + mdb_free(*retp, len * sizeof (uintptr_t)); + + *retp = NULL; + return (-1); + } + + return (0); +} + +static int +read_heap_byte(uint8_t *valp, uintptr_t addr, ssize_t off) +{ + if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) { + v8_warn("failed to read heap value at %p", addr + off); + return (-1); + } + + return (0); +} + +/* + * This is truly horrific. Inside the V8 Script class are a number of + * small-integer fields like the function_token_position (an offset into the + * script's text where the "function" token appears). For 32-bit processes, V8 + * stores these as a sequence of SMI fields, which we know how to interpret well + * enough. For 64-bit processes, "to avoid wasting space", they use a different + * trick: each 8-byte word contains two integer fields. The low word is + * represented like an SMI: shifted left by one. They don't bother shifting the + * high word, since its low bit will never be looked at (since it's not + * word-aligned). + * + * This function is used for cases where we would use read_heap_smi(), except + * that this is one of those fields that might be encoded or might not be, + * depending on whether the address is word-aligned. + */ +static int +read_heap_maybesmi(uintptr_t *valp, uintptr_t addr, ssize_t off) +{ +#ifdef _LP64 + uint32_t readval; + + if (mdb_vread(&readval, sizeof (readval), addr + off) == -1) { + *valp = -1; + v8_warn("failed to read offset %d from %p", off, addr); + return (-1); + } + + /* + * If this was the low half-word, it needs to be shifted right. + */ + if ((addr + off) % sizeof (uintptr_t) == 0) + readval >>= 1; + + *valp = (uintptr_t)readval; + return (0); +#else + return (read_heap_smi(valp, addr, off)); +#endif +} + +/* + * Given a heap object, returns in *valp the byte describing the type of the + * object. This is shorthand for first retrieving the Map at the start of the + * heap object and then retrieving the type byte from the Map object. + */ +static int +read_typebyte(uint8_t *valp, uintptr_t addr) +{ + uintptr_t mapaddr; + ssize_t off = V8_OFF_HEAPOBJECT_MAP; + + if (mdb_vread(&mapaddr, sizeof (mapaddr), addr + off) == -1) { + v8_warn("failed to read type of %p", addr); + return (-1); + } + + if (!V8_IS_HEAPOBJECT(mapaddr)) { + v8_warn("object map is not a heap object\n"); + return (-1); + } + + if (read_heap_byte(valp, mapaddr, V8_OFF_MAP_INSTANCE_ATTRIBUTES) == -1) + return (-1); + + return (0); +} + +/* + * Given a heap object, returns in *valp the size of the object. For + * variable-size objects, returns an undefined value. + */ +static int +read_size(size_t *valp, uintptr_t addr) +{ + uintptr_t mapaddr; + uint8_t size; + + if (read_heap_ptr(&mapaddr, addr, V8_OFF_HEAPOBJECT_MAP) != 0) + return (-1); + + if (!V8_IS_HEAPOBJECT(mapaddr)) { + v8_warn("heap object map is not itself a heap object\n"); + return (-1); + } + + if (read_heap_byte(&size, mapaddr, V8_OFF_MAP_INSTANCE_SIZE) != 0) + return (-1); + + *valp = size << V8_PointerSizeLog2; + return (0); +} + +/* + * Assuming "addr" refers to a FixedArray that is implementing a + * StringDictionary, iterate over its contents calling the specified function + * with key and value. + */ +static int +read_heap_dict(uintptr_t addr, + int (*func)(const char *, uintptr_t, void *), void *arg) +{ + uint8_t type; + uintptr_t len; + char buf[512]; + char *bufp; + int rval = -1; + uintptr_t *dict, ndict, i; + + if (read_heap_array(addr, &dict, &ndict, UM_SLEEP) != 0) + return (-1); + + if (V8_DICT_ENTRY_SIZE < 2) { + v8_warn("dictionary entry size (%d) is too small for a " + "key and value\n", V8_DICT_ENTRY_SIZE); + goto out; + } + + for (i = V8_DICT_START_INDEX + V8_DICT_PREFIX_SIZE; i < ndict; + i += V8_DICT_ENTRY_SIZE) { + /* + * The layout here is key, value, details. (This is hardcoded + * in Dictionary<Shape, Key>::SetEntry().) + */ + if (jsobj_is_undefined(dict[i])) + continue; + + if (V8_IS_SMI(dict[i])) { + intptr_t val = V8_SMI_VALUE(dict[i]); + (void) snprintf(buf, sizeof (buf), "%" PRIdPTR, val); + } else { + if (jsobj_is_hole(dict[i])) { + /* + * In some cases, the key can (apparently) be a + * hole, in which case we skip over it. + */ + continue; + } + + if (read_typebyte(&type, dict[i]) != 0) + goto out; + + if (!V8_TYPE_STRING(type)) + goto out; + + bufp = buf; + len = sizeof (buf); + + if (jsstr_print(dict[i], JSSTR_NUDE, &bufp, &len) != 0) + goto out; + } + + if (func(buf, dict[i + 1], arg) == -1) + goto out; + } + + rval = 0; +out: + mdb_free(dict, ndict * sizeof (uintptr_t)); + + return (rval); +} + +/* + * Given an object, returns in "buf" the name of the constructor function. With + * "verbose", prints the pointer to the JSFunction object. Given anything else, + * returns an error (and warns the user why). + */ +static int +obj_jsconstructor(uintptr_t addr, char **bufp, size_t *lenp, boolean_t verbose) +{ + uint8_t type; + uintptr_t map, consfunc, funcinfop; + const char *constype; + + if (!V8_IS_HEAPOBJECT(addr) || + read_typebyte(&type, addr) != 0 || + (type != V8_TYPE_JSOBJECT && type != V8_TYPE_JSARRAY)) { + mdb_warn("%p is not a JSObject\n", addr); + return (-1); + } + + if (mdb_vread(&map, sizeof (map), addr + V8_OFF_HEAPOBJECT_MAP) == -1 || + mdb_vread(&consfunc, sizeof (consfunc), + map + V8_OFF_MAP_CONSTRUCTOR) == -1) { + mdb_warn("unable to read object map\n"); + return (-1); + } + + if (read_typebyte(&type, consfunc) != 0) + return (-1); + + constype = enum_lookup_str(v8_types, type, ""); + if (strcmp(constype, "Oddball") == 0) { + jsobj_print_t jsop; + bzero(&jsop, sizeof (jsop)); + jsop.jsop_bufp = bufp; + jsop.jsop_lenp = lenp; + return (jsobj_print_oddball(consfunc, &jsop)); + } + + if (strcmp(constype, "JSFunction") != 0) { + mdb_warn("constructor: expected JSFunction, found %s\n", + constype); + return (-1); + } + + if (read_heap_ptr(&funcinfop, consfunc, V8_OFF_JSFUNCTION_SHARED) != 0) + return (-1); + + if (jsfunc_name(funcinfop, bufp, lenp) != 0) + return (-1); + + if (verbose) + bsnprintf(bufp, lenp, " (JSFunction: %p)", consfunc); + + return (0); +} + +/* + * Returns in "buf" a description of the type of "addr" suitable for printing. + */ +static int +obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep) +{ + uint8_t typebyte; + uintptr_t strptr, map, consfunc, funcinfop; + const char *typename; + + if (V8_IS_FAILURE(addr)) { + if (typep) + *typep = 0; + (void) bsnprintf(bufp, lenp, "'Failure' object"); + return (0); + } + + if (V8_IS_SMI(addr)) { + if (typep) + *typep = 0; + (void) bsnprintf(bufp, lenp, "SMI: value = %d", + V8_SMI_VALUE(addr)); + return (0); + } + + if (read_typebyte(&typebyte, addr) != 0) + return (-1); + + if (typep) + *typep = typebyte; + + typename = enum_lookup_str(v8_types, typebyte, "<unknown>"); + (void) bsnprintf(bufp, lenp, typename); + + if (strcmp(typename, "Oddball") == 0) { + if (read_heap_ptr(&strptr, addr, + V8_OFF_ODDBALL_TO_STRING) != -1) { + (void) bsnprintf(bufp, lenp, ": \""); + (void) jsstr_print(strptr, JSSTR_NUDE, bufp, lenp); + (void) bsnprintf(bufp, lenp, "\""); + } + } + + if (strcmp(typename, "JSObject") == 0 && + mdb_vread(&map, sizeof (map), addr + V8_OFF_HEAPOBJECT_MAP) != -1 && + mdb_vread(&consfunc, sizeof (consfunc), + map + V8_OFF_MAP_CONSTRUCTOR) != -1 && + read_typebyte(&typebyte, consfunc) == 0 && + strcmp(enum_lookup_str(v8_types, typebyte, ""), + "JSFunction") == 0 && + mdb_vread(&funcinfop, sizeof (funcinfop), + consfunc + V8_OFF_JSFUNCTION_SHARED) != -1) { + (void) bsnprintf(bufp, lenp, ": "); + (void) jsfunc_name(funcinfop, bufp, lenp); + } + + return (0); +} + +/* + * V8 allows implementers (like Node) to store pointer-sized values into + * internal fields within V8 heap objects. Implementors access these values by + * 0-based index (e.g., SetInternalField(0, value)). These values are stored as + * an array directly after the last actual C++ field in the C++ object. + * + * Node uses internal fields to refer to handles. For example, a socket's C++ + * HandleWrap object is typically stored as internal field 0 in the JavaScript + * Socket object. Similarly, the native-heap-allocated chunk of memory + * associated with a Node Buffer is referenced by field 0 in the External array + * pointed-to by the Node Buffer JSObject. + */ +static int +obj_v8internal(uintptr_t addr, uint_t idx, uintptr_t *valp) +{ + char *bufp; + size_t len; + ssize_t off; + uint8_t type; + + v8_class_t *clp; + char buf[256]; + + bufp = buf; + len = sizeof (buf); + if (obj_jstype(addr, &bufp, &len, &type) != 0) + return (DCMD_ERR); + + if (type == 0) { + mdb_warn("%p: unsupported type\n", addr); + return (DCMD_ERR); + } + + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) { + if (strcmp(buf, clp->v8c_name) == 0) + break; + } + + if (clp == NULL) { + mdb_warn("%p: didn't find expected class\n", addr); + return (DCMD_ERR); + } + + off = clp->v8c_end + (idx * sizeof (uintptr_t)) - 1; + if (read_heap_ptr(valp, addr, off) != 0) { + mdb_warn("%p: failed to read from %p\n", addr, addr + off); + return (DCMD_ERR); + } + + return (DCMD_OK); +} + +/* + * Print out the fields of the given object that come from the given class. + */ +static int +obj_print_fields(uintptr_t baddr, v8_class_t *clp) +{ + v8_field_t *flp; + uintptr_t addr, value; + int rv; + char *bufp; + size_t len; + uint8_t type; + char buf[256]; + + for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) { + bufp = buf; + len = sizeof (buf); + + addr = baddr + V8_OFF_HEAP(flp->v8f_offset); + + if (flp->v8f_isstr) { + if (mdb_readstr(buf, sizeof (buf), addr) == -1) { + mdb_printf("%p %s (unreadable)\n", + addr, flp->v8f_name); + continue; + } + + mdb_printf("%p %s = \"%s\"\n", + addr, flp->v8f_name, buf); + continue; + } + + if (flp->v8f_isbyte) { + uint8_t sv; + if (mdb_vread(&sv, sizeof (sv), addr) == -1) { + mdb_printf("%p %s (unreadable)\n", + addr, flp->v8f_name); + continue; + } + + mdb_printf("%p %s = 0x%x\n", addr, flp->v8f_name, sv); + continue; + } + + rv = mdb_vread((void *)&value, sizeof (value), addr); + + if (rv != sizeof (value) || + obj_jstype(value, &bufp, &len, &type) != 0) { + mdb_printf("%p %s (unreadable)\n", addr, flp->v8f_name); + continue; + } + + if (type != 0 && V8_TYPE_STRING(type)) { + (void) bsnprintf(&bufp, &len, ": "); + (void) jsstr_print(value, JSSTR_QUOTED, &bufp, &len); + } + + mdb_printf("%p %s = %p (%s)\n", addr, flp->v8f_name, value, + buf); + } + + return (DCMD_OK); +} + +/* + * Print out all fields of the given object, starting with the root of the class + * hierarchy and working down the most specific type. + */ +static int +obj_print_class(uintptr_t addr, v8_class_t *clp) +{ + int rv = 0; + + /* + * If we have no fields, we just print a simple inheritance hierarchy. + * If we have fields but our parent doesn't, our header includes the + * inheritance hierarchy. + */ + if (clp->v8c_end == 0) { + mdb_printf("%s ", clp->v8c_name); + + if (clp->v8c_parent != NULL) { + mdb_printf("< "); + (void) obj_print_class(addr, clp->v8c_parent); + } + + return (0); + } + + mdb_printf("%p %s", addr, clp->v8c_name); + + if (clp->v8c_start == 0 && clp->v8c_parent != NULL) { + mdb_printf(" < "); + (void) obj_print_class(addr, clp->v8c_parent); + } + + mdb_printf(" {\n"); + (void) mdb_inc_indent(4); + + if (clp->v8c_start > 0 && clp->v8c_parent != NULL) + rv = obj_print_class(addr, clp->v8c_parent); + + rv |= obj_print_fields(addr, clp); + (void) mdb_dec_indent(4); + mdb_printf("}\n"); + + return (rv); +} + +/* + * Print the ASCII string for the given JS string, expanding ConsStrings and + * ExternalStrings as needed. + */ +static int jsstr_print_seq(uintptr_t, uint_t, char **, size_t *, size_t, + ssize_t); +static int jsstr_print_cons(uintptr_t, uint_t, char **, size_t *); +static int jsstr_print_sliced(uintptr_t, uint_t, char **, size_t *); +static int jsstr_print_external(uintptr_t, uint_t, char **, size_t *); + +static int +jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) +{ + uint8_t typebyte; + int err = 0; + char *lbufp; + size_t llen; + char buf[64]; + boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; + + if (read_typebyte(&typebyte, addr) != 0) { + (void) bsnprintf(bufp, lenp, "<could not read type>"); + return (-1); + } + + if (!V8_TYPE_STRING(typebyte)) { + (void) bsnprintf(bufp, lenp, "<not a string>"); + return (-1); + } + + if (verbose) { + lbufp = buf; + llen = sizeof (buf); + (void) obj_jstype(addr, &lbufp, &llen, NULL); + mdb_printf("%s\n", buf); + (void) mdb_inc_indent(4); + } + + if (JSSTR_DEPTH(flags) > JSSTR_MAXDEPTH) { + (void) bsnprintf(bufp, lenp, "<maximum depth exceeded>"); + return (-1); + } + + if (V8_STRENC_ASCII(typebyte)) + flags |= JSSTR_ISASCII; + else + flags &= ~JSSTR_ISASCII; + + flags = JSSTR_BUMPDEPTH(flags); + + if (V8_STRREP_SEQ(typebyte)) + err = jsstr_print_seq(addr, flags, bufp, lenp, 0, -1); + else if (V8_STRREP_CONS(typebyte)) + err = jsstr_print_cons(addr, flags, bufp, lenp); + else if (V8_STRREP_EXT(typebyte)) + err = jsstr_print_external(addr, flags, bufp, lenp); + else if (V8_STRREP_SLICED(typebyte)) + err = jsstr_print_sliced(addr, flags, bufp, lenp); + else { + (void) bsnprintf(bufp, lenp, "<unknown string type>"); + err = -1; + } + + if (verbose) + (void) mdb_dec_indent(4); + + return (err); +} + +static int +jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp, + size_t sliceoffset, ssize_t slicelen) +{ + /* + * To allow the caller to allocate a very large buffer for strings, + * we'll allocate a buffer sized based on our input, making it at + * least enough space for our ellipsis and at most 256K. + */ + uintptr_t i, nreadoffset, blen, nstrbytes, nstrchrs; + ssize_t nreadbytes; + boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; + boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; + char *buf; + uint16_t chrval; + + if (read_heap_smi(&nstrchrs, addr, V8_OFF_STRING_LENGTH) != 0) { + (void) bsnprintf(bufp, lenp, + "<string (failed to read length)>"); + return (-1); + } + + if (slicelen != -1) + nstrchrs = slicelen; + + blen = ((flags & JSSTR_ISASCII) != 0) ? *lenp : 2 * (*lenp); + if ((blen = MIN(blen, 256 * 1024)) == 0) + return (0); + + if ((flags & JSSTR_ISASCII) != 0) { + nstrbytes = nstrchrs; + nreadoffset = sliceoffset; + nreadbytes = nstrbytes + sizeof ("\"\"") <= *lenp ? + nstrbytes : *lenp - sizeof ("\"\"[...]"); + } else { + nstrbytes = 2 * nstrchrs; + nreadoffset = 2 * sliceoffset; + nreadbytes = nstrchrs + sizeof ("\"\"") <= *lenp ? + nstrbytes : 2 * (*lenp - sizeof ("\"\"[...]")); + } + + if (nreadbytes < 0) { + /* + * We don't even have the room to store the ellipsis; zero + * the buffer out and set the length to zero. + */ + *bufp = '\0'; + *lenp = 0; + return (0); + } + + if (verbose) { + mdb_printf("length: %d chars (%d bytes), " + "will read %d bytes from offset %d\n", + nstrchrs, nstrbytes, nreadbytes, nreadoffset); + mdb_printf("given buffer size: %d, internal buffer: %d\n", + *lenp, blen); + } + + if (nstrbytes == 0) { + (void) bsnprintf(bufp, lenp, "%s%s", + quoted ? "\"" : "", quoted ? "\"" : ""); + return (0); + } + + buf = alloca(blen); + buf[0] = '\0'; + + if ((flags & JSSTR_ISASCII) != 0) { + if (mdb_readstr(buf, nreadbytes + 1, + addr + V8_OFF_SEQASCIISTR_CHARS + nreadoffset) == -1) { + v8_warn("failed to read SeqString data"); + return (-1); + } + + if (nreadbytes != nstrbytes) + (void) strlcat(buf, "[...]", blen); + + (void) bsnprintf(bufp, lenp, "%s%s%s", + quoted ? "\"" : "", buf, quoted ? "\"" : ""); + } else { + if (mdb_readstr(buf, nreadbytes, + addr + V8_OFF_SEQTWOBYTESTR_CHARS + nreadoffset) == -1) { + v8_warn("failed to read SeqTwoByteString data"); + return (-1); + } + + (void) bsnprintf(bufp, lenp, "%s", quoted ? "\"" : ""); + for (i = 0; i < nreadbytes; i += 2) { + /*LINTED*/ + chrval = *((uint16_t *)(buf + i)); + (void) bsnprintf(bufp, lenp, "%c", + (isascii(chrval) || chrval == 0) ? + (char)chrval : '?'); + } + if (nreadbytes != nstrbytes) + (void) bsnprintf(bufp, lenp, "[...]"); + (void) bsnprintf(bufp, lenp, "%s", quoted ? "\"" : ""); + } + + return (0); +} + +static int +jsstr_print_cons(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) +{ + boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; + boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; + uintptr_t ptr1, ptr2; + + if (read_heap_ptr(&ptr1, addr, V8_OFF_CONSSTRING_FIRST) != 0) { + (void) bsnprintf(bufp, lenp, + "<cons string (failed to read first)>"); + return (-1); + } + + if (read_heap_ptr(&ptr2, addr, V8_OFF_CONSSTRING_SECOND) != 0) { + (void) bsnprintf(bufp, lenp, + "<cons string (failed to read second)>"); + return (-1); + } + + if (verbose) { + mdb_printf("ptr1: %p\n", ptr1); + mdb_printf("ptr2: %p\n", ptr2); + } + + if (quoted) + (void) bsnprintf(bufp, lenp, "\""); + + flags = JSSTR_BUMPDEPTH(flags) & ~JSSTR_QUOTED; + + if (jsstr_print(ptr1, flags, bufp, lenp) != 0) + return (-1); + + if (jsstr_print(ptr2, flags, bufp, lenp) != 0) + return (-1); + + if (quoted) + (void) bsnprintf(bufp, lenp, "\""); + + return (0); +} + +static int +jsstr_print_sliced(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) +{ + uintptr_t parent, offset, length; + uint8_t typebyte; + boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE; + boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; + + if (read_heap_ptr(&parent, addr, V8_OFF_SLICEDSTRING_PARENT) != 0) { + (void) bsnprintf(bufp, lenp, + "<sliced string (failed to read parent)>"); + return (-1); + } + + if (read_heap_smi(&offset, addr, V8_OFF_SLICEDSTRING_OFFSET) != 0) { + (void) bsnprintf(bufp, lenp, + "<sliced string (failed to read offset)>"); + return (-1); + } + + if (read_heap_smi(&length, addr, V8_OFF_STRING_LENGTH) != 0) { + (void) bsnprintf(bufp, lenp, + "<sliced string (failed to read length)>"); + return (-1); + } + + if (verbose) + mdb_printf("parent: %p, offset = %d, length = %d\n", + parent, offset, length); + + if (read_typebyte(&typebyte, parent) != 0) { + (void) bsnprintf(bufp, lenp, + "<sliced string (failed to read parent type)>"); + return (0); + } + + if (!V8_STRREP_SEQ(typebyte)) { + (void) bsnprintf(bufp, lenp, + "<sliced string (parent is not a sequential string)>"); + return (0); + } + + if (quoted) + (void) bsnprintf(bufp, lenp, "\""); + + flags = JSSTR_BUMPDEPTH(flags) & ~JSSTR_QUOTED; + + if (V8_STRENC_ASCII(typebyte)) + flags |= JSSTR_ISASCII; + + if (jsstr_print_seq(parent, flags, bufp, lenp, offset, length) != 0) + return (-1); + + if (quoted) + (void) bsnprintf(bufp, lenp, "\""); + + return (0); +} + +static int +jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp) +{ + uintptr_t ptr1, ptr2; + size_t blen = *lenp + 1; + char *buf; + boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE; + int rval = -1; + + if ((flags & JSSTR_ISASCII) == 0) { + (void) bsnprintf(bufp, lenp, "<external two-byte string>"); + return (0); + } + + if (flags & JSSTR_VERBOSE) + mdb_printf("assuming Node.js string\n"); + + if (read_heap_ptr(&ptr1, addr, V8_OFF_EXTERNALSTRING_RESOURCE) != 0) { + (void) bsnprintf(bufp, lenp, + "<external string (failed to read resource)>"); + return (-1); + } + + if (mdb_vread(&ptr2, sizeof (ptr2), + ptr1 + NODE_OFF_EXTSTR_DATA) == -1) { + (void) bsnprintf(bufp, lenp, "<external string (failed to " + "read node external pointer %p)>", + ptr1 + NODE_OFF_EXTSTR_DATA); + return (-1); + } + + buf = mdb_alloc(blen, UM_SLEEP); + + if (mdb_readstr(buf, blen, ptr2) == -1) { + (void) bsnprintf(bufp, lenp, "<external string " + "(failed to read ExternalString data)>"); + goto out; + } + + if (buf[0] != '\0' && !isascii(buf[0])) { + (void) bsnprintf(bufp, lenp, "<external string " + "(failed to read ExternalString ascii data)>"); + goto out; + } + + (void) bsnprintf(bufp, lenp, "%s%s%s", + quoted ? "\"" : "", buf, quoted ? "\"" : ""); + + rval = 0; +out: + mdb_free(buf, blen); + + return (rval); +} + +/* + * Returns true if the given address refers to the named oddball object (e.g. + * "undefined"). Returns false on failure (since we shouldn't fail on the + * actual "undefined" value). + */ +static boolean_t +jsobj_is_oddball(uintptr_t addr, char *oddball) +{ + uint8_t type; + uintptr_t strptr; + const char *typename; + char buf[16]; + char *bufp = buf; + size_t len = sizeof (buf); + + v8_silent++; + + if (read_typebyte(&type, addr) != 0) { + v8_silent--; + return (B_FALSE); + } + + v8_silent--; + typename = enum_lookup_str(v8_types, type, "<unknown>"); + if (strcmp(typename, "Oddball") != 0) + return (B_FALSE); + + if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) == -1) + return (B_FALSE); + + if (jsstr_print(strptr, JSSTR_NUDE, &bufp, &len) != 0) + return (B_FALSE); + + return (strcmp(buf, oddball) == 0); +} + +static boolean_t +jsobj_is_undefined(uintptr_t addr) +{ + return (jsobj_is_oddball(addr, "undefined")); +} + +static boolean_t +jsobj_is_hole(uintptr_t addr) +{ + return (jsobj_is_oddball(addr, "hole")); +} + +/* + * Iterate the properties of a JavaScript object "addr". + * + * Every heap object refers to a Map that describes how that heap object is laid + * out. The Map includes information like the constructor function used to + * create the object, how many bytes each object uses, and how many properties + * are stored inside the object. (A single Map object can be shared by many + * objects of the same general type, which is why this information is encoded by + * reference rather than contained in each object.) + * + * V8 knows about lots of different kinds of properties: + * + * o properties with numeric names (e.g., array elements) + * o dictionary properties + * o "fast" properties stored inside each object, much like a C struct + * o properties stored in the separate "properties" array + * o getters, setters, and other magic (not supported by this module) + * + * While property lookup in JavaScript involves traversing an object's prototype + * chain, this module only iterates the properties local to the object itself. + * + * + * Numeric properties + * + * Properties having numeric indexes are stored in the "elements" array attached + * to each object. Objects with numeric properties can also have other + * properties. + * + * + * Dictionary properties + * + * An object with dictionary properties is identified by one of the bits in + * "bitfield3" in the object's Map. For details on slow properties, see + * read_heap_dict(). + * + * + * Other properties + * + * The Map object refers to an array of "instance descriptors". This array has + * a few metadata entries at the front, followed by groups of three entries for + * each property. In Node v0.10 and later, it looks roughly like this: + * + * +--------------+ +----------------------+ + * | JSObject | +--> | Map | + * +--------------| | +----------------------+ + * | map | ---+ | ... | + * | ... | | instance_descriptors | --+ + * in-object | [prop 0 val] | | ... | | + * properties | [prop 1 val] | +----------------------+ | + * (not for all | ... | | + * objects) | [prop N val] | | + * +--------------+ | + * +------------------------------------------------+ + * | + * +----> +------------------------------+ + * | FixedArray | + * +------------------------------+ + * | ... | + * | prop 0 "key" descriptor | + * | prop 0 "details" descriptor | + * | prop 0 "value" descriptor | + * | prop 1 "key" descriptor | + * | prop 1 "details" descriptor | + * | prop 1 "value" descriptor | + * | ... | + * | prop N "key" descriptor | + * | prop N "details" descriptor | + * | prop N "value" descriptor | + * +------------------------------+ + * + * In versions of Node prior to 0.10, there's an extra level of indirection. + * The Map refers to a "transitions" array, which has an entry that points to + * the instance descriptors. In both cases, the descriptors look roughly the + * same. + * + * Each property is described by three pointer-sized entries: + * + * o key: a string denoting the name of the property + * o details: a bitfield describing attributes of this property + * o value: an integer describing where this property's value is stored + * + * "key" is straightforward: it's just the name of the property as the + * JavaScript programmer knows it. + * + * In versions prior to Node 0.12, "value" is an integer. If "value" is less + * than the number of properties stored inside the object (which is also + * recorded in the Map), then it denotes which of the in-object property value + * slots (shown above inside the JSObject object) stores the value for this + * property. If "value" is greater than the number of properties stored inside + * the object, then it denotes which index into the separate "properties" array + * (a separate field in the JSObject, not shown above) contains the value for + * this property. + * + * In Node 0.12, for properties that are stored inside the object, the offset is + * obtained not using "value", but using a bitfield from the "details" part of + * the descriptor. + * + * Terminology notes: it's important to keep straight the different senses of + * "object" and "property" here. We use "JavaScript objects" to refer to the + * things that JavaScript programmers would call objects, including instances of + * Object and Array and subclasses of those. These are a subset of V8 heap + * objects, since V8 uses its heap to manage lots of other objects that + * JavaScript programmers don't think about. This function iterates JavaScript + * properties of these JavaScript objects, not internal properties of heap + * objects in general. + * + * Relatedly, while JavaScript programmers frequently interchange the notions of + * property names, property values, and property configurations (e.g., getters + * and setters, read-only or not, hidden or not), these are all distinct in the + * implementation of the VM, and "property" typically refers to the whole + * configuration, which may include a way to get the property name and value. + * + * The canonical source of the information used here is the implementation of + * property lookup in the V8 source code, currently in Object::GetProperty. + */ + +static int +jsobj_properties(uintptr_t addr, + int (*func)(const char *, uintptr_t, void *), void *arg, + jspropinfo_t *propinfop) +{ + uintptr_t ptr, map, elements; + uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans, *elts; + size_t size, nprops, ndescs, ncontent, ntrans, len; + ssize_t ii, rndescs; + uint8_t type, ninprops; + int rval = -1; + size_t ps = sizeof (uintptr_t); + ssize_t off; + jspropinfo_t propinfo = JPI_NONE; + + /* + * First, check if the JSObject's "properties" field is a FixedArray. + * If not, then this is something we don't know how to deal with, and + * we'll just pass the caller a NULL value. + */ + if (mdb_vread(&ptr, ps, addr + V8_OFF_JSOBJECT_PROPERTIES) == -1) + return (-1); + + if (read_typebyte(&type, ptr) != 0) + return (-1); + + if (type != V8_TYPE_FIXEDARRAY) { + char buf[256]; + (void) mdb_snprintf(buf, sizeof (buf), "<%s>", + enum_lookup_str(v8_types, type, "unknown")); + if (propinfop != NULL) + *propinfop = JPI_BADLAYOUT; + return (func(buf, NULL, arg)); + } + + /* + * As described above, we need the Map to figure out how to iterate the + * properties for this object. + */ + if (mdb_vread(&map, ps, addr + V8_OFF_HEAPOBJECT_MAP) == -1) + goto err; + + /* + * Check to see if our elements member is an array and non-zero; if + * so, it contains numerically-named properties. Whether or not there + * are any numerically-named properties, there may be other kinds of + * properties. + */ + if (V8_ELEMENTS_KIND_SHIFT != -1 && + read_heap_ptr(&elements, addr, V8_OFF_JSOBJECT_ELEMENTS) == 0 && + read_heap_array(elements, &elts, &len, UM_SLEEP) == 0 && len != 0) { + uint8_t bit_field2, kind; + size_t sz = len * sizeof (uintptr_t); + + if (mdb_vread(&bit_field2, sizeof (bit_field2), + map + V8_OFF_MAP_BIT_FIELD2) == -1) { + mdb_free(elts, sz); + goto err; + } + + kind = bit_field2 >> V8_ELEMENTS_KIND_SHIFT; + kind &= (1 << V8_ELEMENTS_KIND_BITCOUNT) - 1; + propinfo |= JPI_NUMERIC; + + if (kind == V8_ELEMENTS_FAST_ELEMENTS || + kind == V8_ELEMENTS_FAST_HOLEY_ELEMENTS) { + for (ii = 0; ii < len; ii++) { + char name[10]; + + if (kind == V8_ELEMENTS_FAST_HOLEY_ELEMENTS && + jsobj_is_hole(elts[ii])) + continue; + + snprintf(name, sizeof (name), "%" PRIdPTR, ii); + + if (func(name, elts[ii], arg) != 0) { + mdb_free(elts, sz); + goto err; + } + } + } else if (kind == V8_ELEMENTS_DICTIONARY_ELEMENTS) { + propinfo |= JPI_DICT; + if (read_heap_dict(elements, func, arg) != 0) { + mdb_free(elts, sz); + goto err; + } + } + + mdb_free(elts, sz); + } + + if (V8_DICT_SHIFT != -1) { + v8_field_t *flp; + uintptr_t bit_field3; + + /* + * If dictionary properties are supported (the V8_DICT_SHIFT + * offset is not -1), then bitfield 3 tells us if the properties + * for this object are stored in "properties" field of the + * object using a Dictionary representation. + * + * Versions of V8 prior to Node 0.12 treated bit_field3 as an + * SMI, so it was pointer-sized, and it has to be converted from + * an SMI before using it. In 0.12, it's treated as a raw + * uint32_t, meaning it's always int-sized and it should not be + * converted. We can tell which case we're in because the debug + * constant (v8dbg_class_map__bit_field3__TYPE) tells us whether + * the TYPE is "SMI" or "int". + */ + + flp = conf_field_lookup("Map", "bit_field3"); + if (flp == NULL || flp->v8f_isbyte) { + /* + * v8f_isbyte indicates the type is "int", so we're in + * the int-sized not-a-SMI world. + */ + unsigned int bf3_value; + if (mdb_vread(&bf3_value, sizeof (bf3_value), + map + V8_OFF_MAP_BIT_FIELD3) == -1) + goto err; + bit_field3 = (uintptr_t)bf3_value; + } else { + /* The metadata indicates this is an SMI. */ + if (mdb_vread(&bit_field3, sizeof (bit_field3), + map + V8_OFF_MAP_BIT_FIELD3) == -1) + goto err; + bit_field3 = V8_SMI_VALUE(bit_field3); + } + + if (bit_field3 & (1 << V8_DICT_SHIFT)) { + propinfo |= JPI_DICT; + if (propinfop != NULL) + *propinfop = propinfo; + return (read_heap_dict(ptr, func, arg)); + } + } else if (V8_OFF_MAP_INSTANCE_DESCRIPTORS != -1) { + uintptr_t bit_field3; + + if (mdb_vread(&bit_field3, sizeof (bit_field3), + map + V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1) + goto err; + + if (V8_SMI_VALUE(bit_field3) == (1 << V8_ISSHARED_SHIFT)) { + /* + * On versions of V8 prior to that used in 0.10, + * the instance descriptors were overloaded to also + * be bit_field3 -- and there was no way from that + * field to infer a dictionary type. Because we + * can't determine if the map is actually the + * hash_table_map, we assume that if it's an object + * that has kIsShared set, that it is in fact a + * dictionary -- an assumption that is assuredly in + * error in some cases. + */ + propinfo |= JPI_DICT; + if (propinfop != NULL) + *propinfop = propinfo; + return (read_heap_dict(ptr, func, arg)); + } + } + + if (read_heap_array(ptr, &props, &nprops, UM_SLEEP) != 0) + goto err; + + /* + * Check if we're looking at an older version of V8, where the instance + * descriptors are stored not directly in the Map, but in the + * "transitions" array that's stored in the Map. + */ + if (V8_OFF_MAP_INSTANCE_DESCRIPTORS == -1) { + if (V8_OFF_MAP_TRANSITIONS == -1 || + V8_TRANSITIONS_IDX_DESC == -1 || + V8_PROP_IDX_CONTENT != -1) { + mdb_warn("missing instance_descriptors, but did " + "not find expected transitions array metadata; " + "cannot read properties\n"); + goto err; + } + + propinfo |= JPI_HASTRANSITIONS; + off = V8_OFF_MAP_TRANSITIONS; + if (mdb_vread(&ptr, ps, map + off) == -1) + goto err; + + if (read_heap_array(ptr, &trans, &ntrans, UM_SLEEP) != 0) + goto err; + + ptr = trans[V8_TRANSITIONS_IDX_DESC]; + mdb_free(trans, ntrans * sizeof (uintptr_t)); + } else { + off = V8_OFF_MAP_INSTANCE_DESCRIPTORS; + if (mdb_vread(&ptr, ps, map + off) == -1) + goto err; + } + + /* + * Either way, at this point "ptr" should refer to the descriptors + * array. + */ + if (read_heap_array(ptr, &descs, &ndescs, UM_SLEEP) != 0) + goto err; + + /* + * For cases where property values are stored directly inside the object + * ("fast properties"), we need to know the whole size of the object and + * the number of properties in the object in order to calculate the + * correct offset for each property. + */ + if (read_size(&size, addr) != 0) + size = 0; + if (mdb_vread(&ninprops, ps, + map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1) + goto err; + + if (V8_PROP_IDX_CONTENT == -1) { + /* + * On node v0.8 and later, the content is not stored in a + * separate FixedArray, but rather with the descriptors. The + * number of actual properties is the length of the array minus + * the first (non-property) elements divided by the number of + * elements per property. + */ + content = descs; + ncontent = ndescs; + rndescs = ndescs > V8_PROP_IDX_FIRST ? + (ndescs - V8_PROP_IDX_FIRST) / V8_PROP_DESC_SIZE : 0; + } else { + /* + * On older versions, the content is stored in a separate array, + * and there's one entry per property (rather than three). + */ + if (V8_PROP_IDX_CONTENT < ndescs && + read_heap_array(descs[V8_PROP_IDX_CONTENT], &content, + &ncontent, UM_SLEEP) != 0) + goto err; + + rndescs = ndescs - V8_PROP_IDX_FIRST; + propinfo |= JPI_HASCONTENT; + } + + /* + * At this point, we've read all the pieces we need to process the list + * of instance descriptors. + */ + for (ii = 0; ii < rndescs; ii++) { + uintptr_t keyidx, validx, detidx, baseidx; + char buf[1024]; + intptr_t val; + size_t len = sizeof (buf); + char *c = buf; + + if (V8_PROP_IDX_CONTENT != -1) { + /* + * In node versions prior to v0.8, this was hardcoded + * in the V8 implementation, so we hardcode it here + * as well. + */ + keyidx = ii + V8_PROP_IDX_FIRST; + validx = ii << 1; + detidx = (ii << 1) + 1; + } else { + baseidx = V8_PROP_IDX_FIRST + (ii * V8_PROP_DESC_SIZE); + keyidx = baseidx + V8_PROP_DESC_KEY; + validx = baseidx + V8_PROP_DESC_VALUE; + detidx = baseidx + V8_PROP_DESC_DETAILS; + } + + /* + * Ignore cases where our understanding doesn't appear to match + * what's here. + */ + if (detidx >= ncontent) { + propinfo |= JPI_SKIPPED; + v8_warn("property descriptor %d: detidx (%d) " + "out of bounds for content array (length %d)\n", + ii, detidx, ncontent); + continue; + } + + /* + * We only process fields. There are other entries here + * (notably: transitions) that we don't care about (and these + * are not errors). + */ + if (!V8_DESC_ISFIELD(content[detidx])) + continue; + + if (keyidx >= ndescs) { + propinfo |= JPI_SKIPPED; + v8_warn("property descriptor %d: keyidx (%d) " + "out of bounds for descriptor array (length %d)\n", + ii, keyidx, ndescs); + continue; + } + + if (jsstr_print(descs[keyidx], JSSTR_NUDE, &c, &len) != 0) { + propinfo |= JPI_SKIPPED; + continue; + } + + val = (intptr_t)content[validx]; + if (!V8_IS_SMI(val)) { + propinfo |= JPI_SKIPPED; + v8_warn("object %p: property descriptor %d: value " + "index is not an SMI: %p\n", addr, ii, val); + continue; + } + + /* + * The "value" part of each property descriptor tells us whether + * the property value is stored directly in the object or in the + * related "props" array. See JSObject::RawFastPropertyAt() in + * the V8 source. + */ + val = V8_SMI_VALUE(val) - ninprops; + if (val < 0) { + uintptr_t propaddr; + + /* + * The property is stored directly inside the object. + * In Node 0.10, "val - ninprops" is the (negative) + * index of the property counted from the end of the + * object. In that context, -1 refers to the last + * word in the object; -2 refers to the second-last + * word, and so on. + * + * In Node 0.12, we get the 0-based index from the + * first property inside the object by reading certain + * bits from the property descriptor details word. + * These constants are literal here because they're + * literal in the V8 source itself. + */ + if (v8_major > 3 || (v8_major == 3 && v8_minor >= 26)) { + val = V8_PROP_FIELDINDEX(content[detidx]); + propaddr = addr + V8_OFF_HEAP( + size - (ninprops - val) * ps); + } else { + propaddr = addr + V8_OFF_HEAP(size + val * ps); + } + + if (mdb_vread(&ptr, sizeof (ptr), propaddr) == -1) { + propinfo |= JPI_SKIPPED; + v8_warn("object %p: failed to read in-object " + "property at %p", addr, propaddr); + continue; + } + + propinfo |= JPI_INOBJECT; + } else { + /* + * The property is in the separate "props" array. + */ + if (val >= nprops) { + /* + * This can happen when properties are deleted. + * If this value isn't obviously corrupt, we'll + * just silently ignore it. + */ + if (val < rndescs) + continue; + + propinfo |= JPI_SKIPPED; + v8_warn("object %p: property descriptor %d: " + "value index value (%d) out of bounds " + "(%d)\n", addr, ii, val, nprops); + goto err; + } + + propinfo |= JPI_PROPS; + ptr = props[val]; + } + + if (func(buf, ptr, arg) != 0) + goto err; + } + + rval = 0; + if (propinfop != NULL) + *propinfop = propinfo; + +err: + if (props != NULL) + mdb_free(props, nprops * sizeof (uintptr_t)); + + if (descs != NULL) + mdb_free(descs, ndescs * sizeof (uintptr_t)); + + if (content != NULL && V8_PROP_IDX_CONTENT != -1) + mdb_free(content, ncontent * sizeof (uintptr_t)); + + return (rval); +} + +/* + * Given the line endings table in "lendsp", computes the line number for the + * given token position and print the result into "buf". If "lendsp" is + * undefined, prints the token position instead. + */ +static int +jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, + char *buf, size_t buflen, int *lineno) +{ + uintptr_t size, bufsz, lower, upper, ii = 0; + uintptr_t *data; + + if (lineno != NULL) + *lineno = -1; + + if (jsobj_is_undefined(lendsp)) { + /* + * The token position is an SMI, but it comes in as its raw + * value so we can more easily compare it to values in the line + * endings table. If we're just printing the position directly, + * we must convert it here, unless we're checking against the + * "-1" sentinel. + */ + if (tokpos == V8_VALUE_SMI(-1)) + mdb_snprintf(buf, buflen, "unknown position"); + else + mdb_snprintf(buf, buflen, "position %d", + V8_SMI_VALUE(tokpos)); + + if (lineno != NULL) + *lineno = 0; + + return (0); + } + + if (read_heap_smi(&size, lendsp, V8_OFF_FIXEDARRAY_LENGTH) != 0) + return (-1); + + bufsz = size * sizeof (data[0]); + + if ((data = mdb_alloc(bufsz, UM_NOSLEEP)) == NULL) { + v8_warn("failed to alloc %d bytes for FixedArray data", bufsz); + return (-1); + } + + if (mdb_vread(data, bufsz, lendsp + V8_OFF_FIXEDARRAY_DATA) != bufsz) { + v8_warn("failed to read FixedArray data"); + mdb_free(data, bufsz); + return (-1); + } + + lower = 0; + upper = size - 1; + + if (tokpos > data[upper]) { + (void) strlcpy(buf, "position out of range", buflen); + mdb_free(data, bufsz); + + if (lineno != NULL) + *lineno = 0; + + return (0); + } + + if (tokpos <= data[0]) { + (void) strlcpy(buf, "line 1", buflen); + mdb_free(data, bufsz); + + if (lineno != NULL) + *lineno = 1; + + return (0); + } + + while (upper >= 1) { + ii = (lower + upper) >> 1; + if (tokpos > data[ii]) + lower = ii + 1; + else if (tokpos <= data[ii - 1]) + upper = ii - 1; + else + break; + } + + if (lineno != NULL) + *lineno = ii + 1; + + (void) mdb_snprintf(buf, buflen, "line %d", ii + 1); + mdb_free(data, bufsz); + return (0); +} + +/* + * Given a Script object, prints nlines on either side of lineno, with each + * line prefixed by prefix (if non-NULL). + */ +static void +jsfunc_lines(uintptr_t scriptp, + uintptr_t start, uintptr_t end, int nlines, char *prefix) +{ + uintptr_t src; + char *buf, *bufp; + size_t bufsz = 1024, len; + int i, line, slop = 10; + boolean_t newline = B_TRUE; + int startline = -1, endline = -1; + + if (read_heap_ptr(&src, scriptp, V8_OFF_SCRIPT_SOURCE) != 0) + return; + + for (;;) { + if ((buf = mdb_zalloc(bufsz, UM_NOSLEEP)) == NULL) { + mdb_warn("failed to allocate source code " + "buffer of size %d", bufsz); + return; + } + + bufp = buf; + len = bufsz; + + if (jsstr_print(src, JSSTR_NUDE, &bufp, &len) != 0) { + mdb_free(buf, bufsz); + return; + } + + if (len > slop) + break; + + mdb_free(buf, bufsz); + bufsz <<= 1; + } + + if (end >= bufsz) + return; + + /* + * First, take a pass to determine where our lines actually start. + */ + for (i = 0, line = 1; buf[i] != '\0'; i++) { + if (buf[i] == '\n') + line++; + + if (i == start) + startline = line; + + if (i == end) { + endline = line; + break; + } + } + + if (startline == -1 || endline == -1) { + mdb_warn("for script %p, could not determine startline/endline" + " (start %ld, end %ld, nlines %d)\n", + scriptp, start, end, nlines); + mdb_free(buf, bufsz); + return; + } + + for (i = 0, line = 1; buf[i] != '\0'; i++) { + if (buf[i] == '\n') { + line++; + newline = B_TRUE; + } + + if (line < startline - nlines) + continue; + + if (line > endline + nlines) + break; + + mdb_printf("%c", buf[i]); + + if (newline) { + if (line >= startline && line <= endline) + mdb_printf("%<b>"); + + if (prefix != NULL) + mdb_printf(prefix, line); + + if (line >= startline && line <= endline) + mdb_printf("%</b>"); + + newline = B_FALSE; + } + } + + mdb_printf("\n"); + + if (line == endline) + mdb_printf("%</b>"); + + mdb_free(buf, bufsz); +} + +/* + * Given a SharedFunctionInfo object, prints into bufp a name of the function + * suitable for printing. This function attempts to infer a name for anonymous + * functions. + */ +static int +jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp) +{ + uintptr_t ptrp; + char *bufs = *bufp; + + if (read_heap_ptr(&ptrp, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_NAME) != 0) { + (void) bsnprintf(bufp, lenp, + "<function (failed to read SharedFunctionInfo)>"); + return (-1); + } + + if (jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0) + return (-1); + + if (*bufp != bufs) + return (0); + + if (read_heap_ptr(&ptrp, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME) != 0) { + (void) bsnprintf(bufp, lenp, "<anonymous>"); + return (0); + } + + (void) bsnprintf(bufp, lenp, "<anonymous> (as "); + bufs = *bufp; + + if (jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0) + return (-1); + + if (*bufp == bufs) + (void) bsnprintf(bufp, lenp, "<anon>"); + + (void) bsnprintf(bufp, lenp, ")"); + + return (0); +} + +/* + * JavaScript-level object printing + */ + +static int +jsobj_print(uintptr_t addr, jsobj_print_t *jsop) +{ + uint8_t type; + const char *klass; + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + + const struct { + char *name; + int (*func)(uintptr_t, jsobj_print_t *); + } table[] = { + { "HeapNumber", jsobj_print_number }, + { "Oddball", jsobj_print_oddball }, + { "JSObject", jsobj_print_jsobject }, + { "JSArray", jsobj_print_jsarray }, + { "JSFunction", jsobj_print_jsfunction }, + { "JSDate", jsobj_print_jsdate }, + { NULL } + }, *ent; + + if (jsop->jsop_baseaddr != NULL && jsop->jsop_member == NULL) + (void) bsnprintf(bufp, lenp, "%p: ", jsop->jsop_baseaddr); + + if (jsop->jsop_printaddr && jsop->jsop_member == NULL) + (void) bsnprintf(bufp, lenp, "%p: ", addr); + + if (V8_IS_SMI(addr)) { + (void) bsnprintf(bufp, lenp, "%d", V8_SMI_VALUE(addr)); + return (0); + } + + if (!V8_IS_HEAPOBJECT(addr)) { + (void) bsnprintf(bufp, lenp, "<not a heap object>"); + return (-1); + } + + if (read_typebyte(&type, addr) != 0) { + (void) bsnprintf(bufp, lenp, "<couldn't read type>"); + return (-1); + } + + if (V8_TYPE_STRING(type)) { + if (jsstr_print(addr, JSSTR_QUOTED, bufp, lenp) == -1) + return (-1); + + return (0); + } + + klass = enum_lookup_str(v8_types, type, "<unknown>"); + + for (ent = &table[0]; ent->name != NULL; ent++) { + if (strcmp(klass, ent->name) == 0) { + jsop->jsop_descended = B_TRUE; + return (ent->func(addr, jsop)); + } + } + + (void) bsnprintf(bufp, lenp, + "<unknown JavaScript object type \"%s\">", klass); + return (-1); +} + +static int +jsobj_print_number(uintptr_t addr, jsobj_print_t *jsop) +{ + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + double numval; + + if (read_heap_double(&numval, addr, V8_OFF_HEAPNUMBER_VALUE) == -1) + return (-1); + + if (numval == (long long)numval) + (void) bsnprintf(bufp, lenp, "%lld", (long long)numval); + else + (void) bsnprintf(bufp, lenp, "%e", numval); + + return (0); +} + +static int +jsobj_print_oddball(uintptr_t addr, jsobj_print_t *jsop) +{ + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + uintptr_t strptr; + + if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) != 0) + return (-1); + + return (jsstr_print(strptr, JSSTR_NUDE, bufp, lenp)); +} + +static int +jsobj_print_prop(const char *desc, uintptr_t val, void *arg) +{ + jsobj_print_t *jsop = arg, descend; + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + + (void) bsnprintf(bufp, lenp, "%s\n%*s\"%s\": ", jsop->jsop_nprops == 0 ? + "{" : "", jsop->jsop_indent + 4, "", desc); + + descend = *jsop; + descend.jsop_depth--; + descend.jsop_indent += 4; + + (void) jsobj_print(val, &descend); + (void) bsnprintf(bufp, lenp, ","); + + jsop->jsop_nprops++; + + return (0); +} + +static int +jsobj_print_prop_member(const char *desc, uintptr_t val, void *arg) +{ + jsobj_print_t *jsop = arg, descend; + const char *member = jsop->jsop_member, *next = member; + int rv; + + for (; *next != '\0' && *next != '.' && *next != '['; next++) + continue; + + if (*member == '[') { + mdb_warn("cannot use array indexing on an object\n"); + return (-1); + } + + if (strncmp(member, desc, next - member) != 0) + return (0); + + if (desc[next - member] != '\0') + return (0); + + /* + * This property matches the desired member; descend. + */ + descend = *jsop; + + if (*next == '\0') { + descend.jsop_member = NULL; + descend.jsop_found = B_TRUE; + } else { + descend.jsop_member = *next == '.' ? next + 1 : next; + } + + rv = jsobj_print(val, &descend); + jsop->jsop_found = descend.jsop_found; + + return (rv); +} + +static int +jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop) +{ + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + + if (jsop->jsop_member != NULL) + return (jsobj_properties(addr, jsobj_print_prop_member, + jsop, &jsop->jsop_propinfo)); + + if (jsop->jsop_depth == 0) { + (void) bsnprintf(bufp, lenp, "[...]"); + return (0); + } + + jsop->jsop_nprops = 0; + + if (jsobj_properties(addr, jsobj_print_prop, jsop, + &jsop->jsop_propinfo) != 0) + return (-1); + + if (jsop->jsop_nprops > 0) { + (void) bsnprintf(bufp, lenp, "\n%*s", jsop->jsop_indent, ""); + } else if (jsop->jsop_nprops == 0) { + (void) bsnprintf(bufp, lenp, "{"); + } else { + (void) bsnprintf(bufp, lenp, "{ /* unknown property */ "); + } + + (void) bsnprintf(bufp, lenp, "}"); + + return (0); +} + +static int +jsobj_print_jsarray_member(uintptr_t addr, jsobj_print_t *jsop) +{ + uintptr_t *elts; + jsobj_print_t descend; + uintptr_t ptr; + const char *member = jsop->jsop_member, *end, *p; + size_t elt = 0, place = 1, len, rv; + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + + if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) { + (void) bsnprintf(bufp, lenp, + "<array member (failed to read elements)>"); + return (-1); + } + + if (read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0) { + (void) bsnprintf(bufp, lenp, + "<array member (failed to read array)>"); + return (-1); + } + + if (*member != '[') { + mdb_warn("expected bracketed array index; " + "found '%s'\n", member); + return (-1); + } + + if ((end = strchr(member, ']')) == NULL) { + mdb_warn("missing array index terminator\n"); + return (-1); + } + + /* + * We know where our array index ends; convert it to an integer + * by stepping through it from least significant digit to most. + */ + for (p = end - 1; p > member; p--) { + if (*p < '0' || *p > '9') { + mdb_warn("illegal array index at '%c'\n", *p); + return (-1); + } + + elt += (*p - '0') * place; + place *= 10; + } + + if (place == 1) { + mdb_warn("missing array index\n"); + return (-1); + } + + if (elt >= len) { + mdb_warn("array index %d exceeds size of %d\n", elt, len); + return (-1); + } + + descend = *jsop; + + switch (*(++end)) { + case '\0': + descend.jsop_member = NULL; + descend.jsop_found = B_TRUE; + break; + + case '.': + descend.jsop_member = end + 1; + break; + + case '[': + descend.jsop_member = end; + break; + + default: + mdb_warn("illegal character '%c' following " + "array index terminator\n", *end); + return (-1); + } + + rv = jsobj_print(elts[elt], &descend); + jsop->jsop_found = descend.jsop_found; + + return (rv); +} + +static int +jsobj_print_jsarray(uintptr_t addr, jsobj_print_t *jsop) +{ + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + int indent = jsop->jsop_indent; + jsobj_print_t descend; + uintptr_t ptr; + uintptr_t *elts; + size_t ii, len; + + if (jsop->jsop_member != NULL) + return (jsobj_print_jsarray_member(addr, jsop)); + + if (jsop->jsop_depth == 0) { + (void) bsnprintf(bufp, lenp, "[...]"); + return (0); + } + + if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) { + (void) bsnprintf(bufp, lenp, + "<array (failed to read elements)>"); + return (-1); + } + + if (read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0) { + (void) bsnprintf(bufp, lenp, "<array (failed to read array)>"); + return (-1); + } + + if (len == 0) { + (void) bsnprintf(bufp, lenp, "[]"); + return (0); + } + + descend = *jsop; + descend.jsop_depth--; + descend.jsop_indent += 4; + + if (len == 1) { + (void) bsnprintf(bufp, lenp, "[ "); + (void) jsobj_print(elts[0], &descend); + (void) bsnprintf(bufp, lenp, " ]"); + return (0); + } + + (void) bsnprintf(bufp, lenp, "[\n"); + + for (ii = 0; ii < len && *lenp > 0; ii++) { + (void) bsnprintf(bufp, lenp, "%*s", indent + 4, ""); + (void) jsobj_print(elts[ii], &descend); + (void) bsnprintf(bufp, lenp, ",\n"); + } + + (void) bsnprintf(bufp, lenp, "%*s", indent, ""); + (void) bsnprintf(bufp, lenp, "]"); + + return (0); +} + +static int +jsobj_print_jsfunction(uintptr_t addr, jsobj_print_t *jsop) +{ + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + uintptr_t shared; + + if (read_heap_ptr(&shared, addr, V8_OFF_JSFUNCTION_SHARED) != 0) + return (-1); + + (void) bsnprintf(bufp, lenp, "function "); + return (jsfunc_name(shared, bufp, lenp) != 0); +} + +static int +jsobj_print_jsdate(uintptr_t addr, jsobj_print_t *jsop) +{ + char **bufp = jsop->jsop_bufp; + size_t *lenp = jsop->jsop_lenp; + char buf[128]; + uintptr_t value; + uint8_t type; + double numval; + + if (V8_OFF_JSDATE_VALUE == -1) { + (void) bsnprintf(bufp, lenp, "<JSDate>", buf); + return (0); + } + + if (read_heap_ptr(&value, addr, V8_OFF_JSDATE_VALUE) != 0) { + (void) bsnprintf(bufp, lenp, "<JSDate (failed to read value)>"); + return (-1); + } + + if (read_typebyte(&type, value) != 0) { + (void) bsnprintf(bufp, lenp, "<JSDate (failed to read type)>"); + return (-1); + } + + if (strcmp(enum_lookup_str(v8_types, type, ""), "HeapNumber") != 0) + return (-1); + + if (read_heap_double(&numval, value, V8_OFF_HEAPNUMBER_VALUE) == -1) { + (void) bsnprintf(bufp, lenp, "<JSDate (failed to read num)>"); + return (-1); + } + + mdb_snprintf(buf, sizeof (buf), "%Y", + (time_t)((long long)numval / MILLISEC)); + (void) bsnprintf(bufp, lenp, "%lld (%s)", (long long)numval, buf); + + return (0); +} + +/* + * dcmd implementations + */ + +/* ARGSUSED */ +static int +dcmd_v8classes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + v8_class_t *clp; + + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) + mdb_printf("%s\n", clp->v8c_name); + + return (DCMD_OK); +} + +static int +do_v8code(uintptr_t addr, boolean_t opt_d) +{ + uintptr_t instrlen; + ssize_t instroff = V8_OFF_CODE_INSTRUCTION_START; + + if (read_heap_ptr(&instrlen, addr, V8_OFF_CODE_INSTRUCTION_SIZE) != 0) + return (DCMD_ERR); + + mdb_printf("code: %p\n", addr); + mdb_printf("instructions: [%p, %p)\n", addr + instroff, + addr + instroff + instrlen); + + if (!opt_d) + return (DCMD_OK); + + mdb_set_dot(addr + instroff); + + do { + (void) mdb_inc_indent(8); /* gets reset by mdb_eval() */ + + /* + * This is absolutely awful. We want to disassemble the above + * range of instructions. Because we don't know how many there + * are, we can't use "::dis". We resort to evaluating "./i", + * but then we need to advance "." by the size of the + * instruction just printed. The only way to do that is by + * printing out "+", but we don't want that to show up, so we + * redirect it to /dev/null. + */ + if (mdb_eval("/i") != 0 || + mdb_eval("+=p ! cat > /dev/null") != 0) { + (void) mdb_dec_indent(8); + v8_warn("failed to disassemble at %p", mdb_get_dot()); + return (DCMD_ERR); + } + } while (mdb_get_dot() < addr + instroff + instrlen); + + (void) mdb_dec_indent(8); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_v8code(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + boolean_t opt_d = B_FALSE; + + if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d, + NULL) != argc) + return (DCMD_USAGE); + + return (do_v8code(addr, opt_d)); +} + +/* ARGSUSED */ +static int +dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uint8_t type; + uintptr_t funcinfop, scriptp, lendsp, tokpos, namep, codep; + char *bufp; + size_t len; + boolean_t opt_d = B_FALSE; + char buf[512]; + + if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d, + NULL) != argc) + return (DCMD_USAGE); + + v8_warnings++; + + if (read_typebyte(&type, addr) != 0) + goto err; + + if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0) { + v8_warn("%p is not an instance of JSFunction\n", addr); + goto err; + } + + if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || + read_heap_maybesmi(&tokpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 || + read_heap_ptr(&scriptp, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&namep, scriptp, V8_OFF_SCRIPT_NAME) != 0 || + read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0) + goto err; + + /* + * The token position is normally a SMI, so read_heap_maybesmi() will + * interpret the value for us. However, this code uses its SMI-encoded + * value, so convert it back here. + */ + tokpos = V8_VALUE_SMI(tokpos); + + bufp = buf; + len = sizeof (buf); + if (jsfunc_name(funcinfop, &bufp, &len) != 0) + goto err; + + mdb_printf("%p: JSFunction: %s\n", addr, buf); + + bufp = buf; + len = sizeof (buf); + mdb_printf("defined at "); + + if (jsstr_print(namep, JSSTR_NUDE, &bufp, &len) == 0) + mdb_printf("%s ", buf); + + if (jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), NULL) == 0) + mdb_printf("%s", buf); + + mdb_printf("\n"); + + if (read_heap_ptr(&codep, + funcinfop, V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0) + goto err; + + v8_warnings--; + + return (do_v8code(codep, opt_d)); + +err: + v8_warnings--; + return (DCMD_ERR); +} + +/* + * Access an internal field of a V8 object. + */ +/* ARGSUSED */ +static int +dcmd_v8internal(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uintptr_t idx; + uintptr_t fieldaddr; + + if (mdb_getopts(argc, argv, NULL) != argc - 1 || + argv[argc - 1].a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + idx = mdb_strtoull(argv[argc - 1].a_un.a_str); + if (obj_v8internal(addr, idx, &fieldaddr) != 0) + return (DCMD_ERR); + + mdb_printf("%p\n", fieldaddr); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_v8frametypes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + enum_print(v8_frametypes); + return (DCMD_OK); +} + +static void +dcmd_v8print_help(void) +{ + mdb_printf( + "Prints out \".\" (a V8 heap object) as an instance of its C++\n" + "class. With no arguments, the appropriate class is detected\n" + "automatically. The 'class' argument overrides this to print an\n" + "object as an instance of the given class. The list of known\n" + "classes can be viewed with ::jsclasses."); +} + +/* ARGSUSED */ +static int +dcmd_v8print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + const char *rqclass; + v8_class_t *clp; + char *bufp; + size_t len; + uint8_t type; + char buf[256]; + + if (argc < 1) { + /* + * If no type was specified, determine it automatically. + */ + bufp = buf; + len = sizeof (buf); + if (obj_jstype(addr, &bufp, &len, &type) != 0) + return (DCMD_ERR); + + if (type == 0) { + /* For SMI or Failure, just print out the type. */ + mdb_printf("%s\n", buf); + return (DCMD_OK); + } + + if ((rqclass = enum_lookup_str(v8_types, type, NULL)) == NULL) { + v8_warn("object has unknown type\n"); + return (DCMD_ERR); + } + } else { + if (argv[0].a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + rqclass = argv[0].a_un.a_str; + } + + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) { + if (strcmp(rqclass, clp->v8c_name) == 0) + break; + } + + if (clp == NULL) { + v8_warn("unknown class '%s'\n", rqclass); + return (DCMD_USAGE); + } + + return (obj_print_class(addr, clp)); +} + +/* ARGSUSED */ +static int +dcmd_v8type(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + char buf[64]; + char *bufp = buf; + size_t len = sizeof (buf); + + if (obj_jstype(addr, &bufp, &len, NULL) != 0) + return (DCMD_ERR); + + mdb_printf("0x%p: %s\n", addr, buf); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_v8types(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + enum_print(v8_types); + return (DCMD_OK); +} + +static int +load_current_context(uintptr_t *fpp, uintptr_t *raddrp) +{ + mdb_reg_t regfp, regip; + +#ifdef __amd64 + if (mdb_getareg(1, "rbp", ®fp) != 0 || + mdb_getareg(1, "rip", ®ip) != 0) { +#else +#ifdef __i386 + if (mdb_getareg(1, "ebp", ®fp) != 0 || + mdb_getareg(1, "eip", ®ip) != 0) { +#else +#error Unrecognized microprocessor +#endif +#endif + v8_warn("failed to load current context"); + return (-1); + } + + if (fpp != NULL) + *fpp = (uintptr_t)regfp; + + if (raddrp != NULL) + *raddrp = (uintptr_t)regip; + + return (0); +} + +typedef struct jsframe { + boolean_t jsf_showall; /* show hidden frames and pointers */ + boolean_t jsf_verbose; /* show arguments and JS code */ + char *jsf_func; /* filter frames for named function */ + char *jsf_prop; /* filter arguments */ + uintptr_t jsf_nlines; /* lines of context (for verbose) */ + uint_t jsf_nskipped; /* skipped frames */ +} jsframe_t; + +static void +jsframe_skip(jsframe_t *jsf) +{ + jsf->jsf_nskipped++; +} + +static void +jsframe_print_skipped(jsframe_t *jsf) +{ + if (jsf->jsf_nskipped == 1) + mdb_printf(" (1 internal frame elided)\n"); + else if (jsf->jsf_nskipped > 1) + mdb_printf(" (%d internal frames elided)\n", + jsf->jsf_nskipped); + jsf->jsf_nskipped = 0; +} + +static int +do_jsframe_special(uintptr_t fptr, uintptr_t raddr, jsframe_t *jsf) +{ + uint_t count; + uintptr_t ftype; + const char *ftypename; + char *prop = jsf->jsf_prop; + + /* + * First see if this looks like a native frame rather than a JavaScript + * frame. We check this by asking MDB to print the return address + * symbolically. If that works, we assume this was NOT a V8 frame, + * since those are never in the symbol table. + */ + count = mdb_snprintf(NULL, 0, "%A", raddr); + if (count > 1) { + if (prop != NULL) + return (0); + + jsframe_print_skipped(jsf); + if (jsf->jsf_showall) { + mdb_printf("%p %a\n", fptr, raddr); + } else if (count <= 65) { + mdb_printf("native: %a\n", raddr); + } else { + char buf[65]; + mdb_snprintf(buf, sizeof (buf), "%a", raddr); + mdb_printf("native: %s...\n", buf); + } + return (0); + } + + /* + * Figure out what kind of frame this is using the same algorithm as + * V8's ComputeType function. First, look for an ArgumentsAdaptorFrame. + */ + if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_CONTEXT) != -1 && + V8_IS_SMI(ftype) && + (ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype), + NULL)) != NULL && strstr(ftypename, "ArgumentsAdaptor") != NULL) { + if (prop != NULL) + return (0); + + if (jsf->jsf_showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename); + } else { + jsframe_skip(jsf); + } + return (0); + } + + /* + * Other special frame types are indicated by a marker. + */ + if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_MARKER) != -1 && + V8_IS_SMI(ftype)) { + if (prop != NULL) + return (0); + + ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype), + NULL); + + if (jsf->jsf_showall && ftypename != NULL) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename); + } else { + jsframe_skip(jsf); + } + + return (0); + } + + return (-1); +} + +static int +do_jsframe(uintptr_t fptr, uintptr_t raddr, jsframe_t *jsf) +{ + boolean_t showall = jsf->jsf_showall; + boolean_t verbose = jsf->jsf_verbose; + const char *func = jsf->jsf_func; + const char *prop = jsf->jsf_prop; + uintptr_t nlines = jsf->jsf_nlines; + + uintptr_t funcp, funcinfop, tokpos, endpos, scriptp, lendsp, ptrp; + uintptr_t ii, nargs; + const char *typename; + char *bufp; + size_t len; + uint8_t type; + char buf[256]; + int lineno; + + /* + * Check for non-JavaScript frames first. + */ + if (func == NULL && do_jsframe_special(fptr, raddr, jsf) == 0) + return (DCMD_OK); + + /* + * At this point we assume we're looking at a JavaScript frame. As with + * native frames, fish the address out of the parent frame. + */ + if (mdb_vread(&funcp, sizeof (funcp), + fptr + V8_OFF_FP_FUNCTION) == -1) { + v8_warn("failed to read stack at %p", + fptr + V8_OFF_FP_FUNCTION); + return (DCMD_ERR); + } + + /* + * Check if this thing is really a JSFunction at all. For some frames, + * it's a Code object, presumably indicating some internal frame. + */ + if (read_typebyte(&type, funcp) != 0 || + (typename = enum_lookup_str(v8_types, type, NULL)) == NULL) { + if (func != NULL || prop != NULL) + return (DCMD_OK); + + if (showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a\n", fptr, raddr); + } else { + jsframe_skip(jsf); + } + return (DCMD_OK); + } + + if (strcmp("Code", typename) == 0) { + if (func != NULL || prop != NULL) + return (DCMD_OK); + + if (showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a internal (Code: %p)\n", + fptr, raddr, funcp); + } else { + jsframe_skip(jsf); + } + return (DCMD_OK); + } + + if (strcmp("JSFunction", typename) != 0) { + if (func != NULL || prop != NULL) + return (DCMD_OK); + + if (showall) { + jsframe_print_skipped(jsf); + mdb_printf("%p %a unknown (%s: %p)", + fptr, raddr, typename, funcp); + } else { + jsframe_skip(jsf); + } + return (DCMD_OK); + } + + if (read_heap_ptr(&funcinfop, funcp, V8_OFF_JSFUNCTION_SHARED) != 0) + return (DCMD_ERR); + + bufp = buf; + len = sizeof (buf); + if (jsfunc_name(funcinfop, &bufp, &len) != 0) + return (DCMD_ERR); + + if (func != NULL && strcmp(buf, func) != 0) + return (DCMD_OK); + + if (prop == NULL) { + jsframe_print_skipped(jsf); + if (showall) + mdb_printf("%p %a ", fptr, raddr); + else + mdb_printf("js: "); + mdb_printf("%s", buf); + if (showall) + mdb_printf(" (JSFunction: %p)\n", funcp); + else + mdb_printf("\n"); + } + + if (!verbose && prop == NULL) + return (DCMD_OK); + + if (verbose) + jsframe_print_skipped(jsf); + + /* + * Although the token position is technically an SMI, we're going to + * byte-compare it to other SMI values so we don't want decode it here. + */ + if (read_heap_maybesmi(&tokpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0) + return (DCMD_ERR); + tokpos = V8_VALUE_SMI(tokpos); + + if (read_heap_ptr(&scriptp, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0) + return (DCMD_ERR); + + if (read_heap_ptr(&ptrp, scriptp, V8_OFF_SCRIPT_NAME) != 0) + return (DCMD_ERR); + + bufp = buf; + len = sizeof (buf); + (void) jsstr_print(ptrp, JSSTR_NUDE, &bufp, &len); + + if (prop != NULL && strcmp(prop, "file") == 0) { + mdb_printf("%s\n", buf); + return (DCMD_OK); + } + + if (prop == NULL) { + (void) mdb_inc_indent(10); + mdb_printf("file: %s\n", buf); + } + + if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0) + return (DCMD_ERR); + + (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf), &lineno); + + if (prop != NULL && strcmp(prop, "posn") == 0) { + mdb_printf("%s\n", buf); + return (DCMD_OK); + } + + if (prop == NULL) + mdb_printf("posn: %s\n", buf); + + if (read_heap_maybesmi(&nargs, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) { + uintptr_t argptr; + char arg[10]; + + if (mdb_vread(&argptr, sizeof (argptr), + fptr + V8_OFF_FP_ARGS + nargs * sizeof (uintptr_t)) != -1 && + argptr != NULL) { + (void) snprintf(arg, sizeof (arg), "this"); + if (prop != NULL && strcmp(arg, prop) == 0) { + mdb_printf("%p\n", argptr); + return (DCMD_OK); + } + + if (prop == NULL) { + bufp = buf; + len = sizeof (buf); + (void) obj_jstype(argptr, &bufp, &len, NULL); + + mdb_printf("this: %p (%s)\n", argptr, buf); + } + } + + for (ii = 0; ii < nargs; ii++) { + if (mdb_vread(&argptr, sizeof (argptr), + fptr + V8_OFF_FP_ARGS + (nargs - ii - 1) * + sizeof (uintptr_t)) == -1) + continue; + + (void) snprintf(arg, sizeof (arg), "arg%" PRIuPTR, + ii + 1); + + if (prop != NULL) { + if (strcmp(arg, prop) != 0) + continue; + + mdb_printf("%p\n", argptr); + return (DCMD_OK); + } + + bufp = buf; + len = sizeof (buf); + (void) obj_jstype(argptr, &bufp, &len, NULL); + + mdb_printf("arg%d: %p (%s)\n", (ii + 1), argptr, buf); + } + } + + + if (prop != NULL) { + mdb_warn("unknown frame property '%s'\n", prop); + return (DCMD_ERR); + } + + if (nlines != 0 && read_heap_maybesmi(&endpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) == 0) { + jsfunc_lines(scriptp, + V8_SMI_VALUE(tokpos), endpos, nlines, "%5d "); + mdb_printf("\n"); + } + + (void) mdb_dec_indent(10); + + return (DCMD_OK); +} + +typedef struct findjsobjects_prop { + struct findjsobjects_prop *fjsp_next; + char fjsp_desc[1]; +} findjsobjects_prop_t; + +typedef struct findjsobjects_instance { + uintptr_t fjsi_addr; + struct findjsobjects_instance *fjsi_next; +} findjsobjects_instance_t; + +typedef struct findjsobjects_obj { + findjsobjects_prop_t *fjso_props; + findjsobjects_prop_t *fjso_last; + jspropinfo_t fjso_propinfo; + size_t fjso_nprops; + findjsobjects_instance_t fjso_instances; + int fjso_ninstances; + avl_node_t fjso_node; + struct findjsobjects_obj *fjso_next; + boolean_t fjso_malformed; + char fjso_constructor[80]; +} findjsobjects_obj_t; + +typedef struct findjsobjects_func { + findjsobjects_instance_t fjsf_instances; + int fjsf_ninstances; + avl_node_t fjsf_node; + struct findjsobjects_func *fjsf_next; + uintptr_t fjsf_shared; + char fjsf_funcname[40]; + char fjsf_scriptname[80]; + char fjsf_location[20]; +} findjsobjects_func_t; + +typedef struct findjsobjects_stats { + int fjss_heapobjs; + int fjss_cached; + int fjss_typereads; + int fjss_jsobjs; + int fjss_objects; + int fjss_arrays; + int fjss_uniques; + int fjss_funcs; + int fjss_funcs_skipped; + int fjss_funcs_unique; +} findjsobjects_stats_t; + +typedef struct findjsobjects_reference { + uintptr_t fjsrf_addr; + char *fjsrf_desc; + size_t fjsrf_index; + struct findjsobjects_reference *fjsrf_next; +} findjsobjects_reference_t; + +typedef struct findjsobjects_referent { + avl_node_t fjsr_node; + uintptr_t fjsr_addr; + findjsobjects_reference_t *fjsr_head; + findjsobjects_reference_t *fjsr_tail; + struct findjsobjects_referent *fjsr_next; +} findjsobjects_referent_t; + +typedef struct findjsobjects_state { + uintptr_t fjs_addr; + uintptr_t fjs_size; + boolean_t fjs_verbose; + boolean_t fjs_brk; + boolean_t fjs_allobjs; + boolean_t fjs_initialized; + boolean_t fjs_marking; + boolean_t fjs_referred; + avl_tree_t fjs_tree; + avl_tree_t fjs_referents; + avl_tree_t fjs_funcinfo; + findjsobjects_referent_t *fjs_head; + findjsobjects_referent_t *fjs_tail; + findjsobjects_obj_t *fjs_current; + findjsobjects_obj_t *fjs_objects; + findjsobjects_func_t *fjs_funcs; + findjsobjects_stats_t fjs_stats; +} findjsobjects_state_t; + +findjsobjects_obj_t * +findjsobjects_alloc(uintptr_t addr) +{ + findjsobjects_obj_t *obj; + + obj = mdb_zalloc(sizeof (findjsobjects_obj_t), UM_SLEEP); + obj->fjso_instances.fjsi_addr = addr; + obj->fjso_ninstances = 1; + + return (obj); +} + +void +findjsobjects_free(findjsobjects_obj_t *obj) +{ + findjsobjects_prop_t *prop, *next; + + for (prop = obj->fjso_props; prop != NULL; prop = next) { + next = prop->fjsp_next; + mdb_free(prop, sizeof (findjsobjects_prop_t) + + strlen(prop->fjsp_desc)); + } + + mdb_free(obj, sizeof (findjsobjects_obj_t)); +} + +int +findjsobjects_cmp(findjsobjects_obj_t *lhs, findjsobjects_obj_t *rhs) +{ + findjsobjects_prop_t *lprop, *rprop; + int rv; + + lprop = lhs->fjso_props; + rprop = rhs->fjso_props; + + while (lprop != NULL && rprop != NULL) { + if ((rv = strcmp(lprop->fjsp_desc, rprop->fjsp_desc)) != 0) + return (rv > 0 ? 1 : -1); + + lprop = lprop->fjsp_next; + rprop = rprop->fjsp_next; + } + + if (lprop != NULL) + return (1); + + if (rprop != NULL) + return (-1); + + if (lhs->fjso_nprops > rhs->fjso_nprops) + return (1); + + if (lhs->fjso_nprops < rhs->fjso_nprops) + return (-1); + + rv = strcmp(lhs->fjso_constructor, rhs->fjso_constructor); + + return (rv < 0 ? -1 : rv > 0 ? 1 : 0); +} + +int +findjsobjects_cmp_funcinfo(findjsobjects_func_t *lhs, + findjsobjects_func_t *rhs) +{ + int diff = lhs->fjsf_shared - rhs->fjsf_shared; + return (diff < 0 ? -1 : diff > 0 ? 1 : 0); +} + +int +findjsobjects_cmp_referents(findjsobjects_referent_t *lhs, + findjsobjects_referent_t *rhs) +{ + if (lhs->fjsr_addr < rhs->fjsr_addr) + return (-1); + + if (lhs->fjsr_addr > rhs->fjsr_addr) + return (1); + + return (0); +} + +int +findjsobjects_cmp_ninstances(const void *l, const void *r) +{ + findjsobjects_obj_t *lhs = *((findjsobjects_obj_t **)l); + findjsobjects_obj_t *rhs = *((findjsobjects_obj_t **)r); + size_t lprod = lhs->fjso_ninstances * lhs->fjso_nprops; + size_t rprod = rhs->fjso_ninstances * rhs->fjso_nprops; + + if (lprod < rprod) + return (-1); + + if (lprod > rprod) + return (1); + + if (lhs->fjso_ninstances < rhs->fjso_ninstances) + return (-1); + + if (lhs->fjso_ninstances > rhs->fjso_ninstances) + return (1); + + if (lhs->fjso_nprops < rhs->fjso_nprops) + return (-1); + + if (lhs->fjso_nprops > rhs->fjso_nprops) + return (1); + + return (0); +} + +/*ARGSUSED*/ +int +findjsobjects_prop(const char *desc, uintptr_t val, void *arg) +{ + findjsobjects_state_t *fjs = arg; + findjsobjects_obj_t *current = fjs->fjs_current; + findjsobjects_prop_t *prop; + + if (desc == NULL) + desc = "<unknown>"; + + prop = mdb_zalloc(sizeof (findjsobjects_prop_t) + + strlen(desc), UM_SLEEP); + + strcpy(prop->fjsp_desc, desc); + + if (current->fjso_last != NULL) { + current->fjso_last->fjsp_next = prop; + } else { + current->fjso_props = prop; + } + + current->fjso_last = prop; + current->fjso_nprops++; + current->fjso_malformed = + val == NULL && current->fjso_nprops == 1 && desc[0] == '<'; + + return (0); +} + +static void +findjsobjects_constructor(findjsobjects_obj_t *obj) +{ + char *bufp = obj->fjso_constructor; + size_t len = sizeof (obj->fjso_constructor); + uintptr_t map, funcinfop; + uintptr_t addr = obj->fjso_instances.fjsi_addr; + uint8_t type; + + v8_silent++; + + if (read_heap_ptr(&map, addr, V8_OFF_HEAPOBJECT_MAP) != 0 || + read_heap_ptr(&addr, map, V8_OFF_MAP_CONSTRUCTOR) != 0) + goto out; + + if (read_typebyte(&type, addr) != 0) + goto out; + + if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0) + goto out; + + if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0) + goto out; + + if (jsfunc_name(funcinfop, &bufp, &len) != 0) + goto out; +out: + v8_silent--; +} + +static void +findjsobjects_jsfunc(findjsobjects_state_t *fjs, uintptr_t addr) +{ + findjsobjects_func_t *func, *ofunc; + findjsobjects_instance_t *inst; + uintptr_t funcinfo, script, name; + avl_index_t where; + int err; + char *bufp; + size_t len; + + /* + * This may be somewhat expensive to do for all JSFunctions, but in most + * core files, there aren't that many. We could defer some of this work + * until the user tries to print the function ::jsfunctions, but this + * step is useful to do early to filter out garbage data. + */ + + v8_silent++; + if (read_heap_ptr(&funcinfo, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || + read_heap_ptr(&script, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&name, script, V8_OFF_SCRIPT_NAME) != 0) { + fjs->fjs_stats.fjss_funcs_skipped++; + v8_silent--; + return; + } + + func = mdb_zalloc(sizeof (findjsobjects_func_t), UM_SLEEP); + func->fjsf_ninstances = 1; + func->fjsf_instances.fjsi_addr = addr; + func->fjsf_shared = funcinfo; + + bufp = func->fjsf_funcname; + len = sizeof (func->fjsf_funcname); + err = jsfunc_name(funcinfo, &bufp, &len); + + bufp = func->fjsf_scriptname; + len = sizeof (func->fjsf_scriptname); + err |= jsstr_print(name, JSSTR_NUDE, &bufp, &len); + + v8_silent--; + if (err != 0) { + fjs->fjs_stats.fjss_funcs_skipped++; + mdb_free(func, sizeof (findjsobjects_func_t)); + return; + } + + fjs->fjs_stats.fjss_funcs++; + ofunc = avl_find(&fjs->fjs_funcinfo, func, &where); + if (ofunc == NULL) { + avl_add(&fjs->fjs_funcinfo, func); + func->fjsf_next = fjs->fjs_funcs; + fjs->fjs_funcs = func; + fjs->fjs_stats.fjss_funcs_unique++; + } else { + inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP); + inst->fjsi_addr = addr; + inst->fjsi_next = ofunc->fjsf_instances.fjsi_next; + ofunc->fjsf_instances.fjsi_next = inst; + ofunc->fjsf_ninstances++; + mdb_free(func, sizeof (findjsobjects_func_t)); + } +} + +int +findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size) +{ + uintptr_t limit; + findjsobjects_stats_t *stats = &fjs->fjs_stats; + uint8_t type; + int jsobject = V8_TYPE_JSOBJECT, jsarray = V8_TYPE_JSARRAY; + int jsfunction = V8_TYPE_JSFUNCTION; + caddr_t range = mdb_alloc(size, UM_SLEEP); + uintptr_t base = addr, mapaddr; + + if (mdb_vread(range, size, addr) == -1) + return (0); + + for (limit = addr + size; addr < limit; addr++) { + findjsobjects_instance_t *inst; + findjsobjects_obj_t *obj; + avl_index_t where; + + if (V8_IS_SMI(addr)) + continue; + + if (!V8_IS_HEAPOBJECT(addr)) + continue; + + stats->fjss_heapobjs++; + + mapaddr = *((uintptr_t *)((uintptr_t)range + + (addr - base) + V8_OFF_HEAPOBJECT_MAP)); + + if (!V8_IS_HEAPOBJECT(mapaddr)) + continue; + + mapaddr += V8_OFF_MAP_INSTANCE_ATTRIBUTES; + stats->fjss_typereads++; + + if (mapaddr >= base && mapaddr < base + size) { + stats->fjss_cached++; + + type = *((uint8_t *)((uintptr_t)range + + (mapaddr - base))); + } else { + if (mdb_vread(&type, sizeof (uint8_t), mapaddr) == -1) + continue; + } + + if (type == jsfunction) { + findjsobjects_jsfunc(fjs, addr); + continue; + } + + if (type != jsobject && type != jsarray) + continue; + + stats->fjss_jsobjs++; + + fjs->fjs_current = findjsobjects_alloc(addr); + + if (type == jsobject) { + if (jsobj_properties(addr, + findjsobjects_prop, fjs, + &fjs->fjs_current->fjso_propinfo) != 0) { + findjsobjects_free(fjs->fjs_current); + fjs->fjs_current = NULL; + continue; + } + + findjsobjects_constructor(fjs->fjs_current); + stats->fjss_objects++; + } else { + uintptr_t ptr; + size_t *nprops = &fjs->fjs_current->fjso_nprops; + ssize_t len = V8_OFF_JSARRAY_LENGTH; + ssize_t elems = V8_OFF_JSOBJECT_ELEMENTS; + ssize_t flen = V8_OFF_FIXEDARRAY_LENGTH; + uintptr_t nelems; + uint8_t t; + + if (read_heap_smi(nprops, addr, len) != 0 || + read_heap_ptr(&ptr, addr, elems) != 0 || + !V8_IS_HEAPOBJECT(ptr) || + read_typebyte(&t, ptr) != 0 || + t != V8_TYPE_FIXEDARRAY || + read_heap_smi(&nelems, ptr, flen) != 0 || + nelems < *nprops) { + findjsobjects_free(fjs->fjs_current); + fjs->fjs_current = NULL; + continue; + } + + strcpy(fjs->fjs_current->fjso_constructor, "Array"); + stats->fjss_arrays++; + } + + /* + * Now determine if we already have an object matching our + * properties. If we don't, we'll add our new object; if we + * do we'll merely enqueue our instance. + */ + obj = avl_find(&fjs->fjs_tree, fjs->fjs_current, &where); + + if (obj == NULL) { + avl_add(&fjs->fjs_tree, fjs->fjs_current); + fjs->fjs_current->fjso_next = fjs->fjs_objects; + fjs->fjs_objects = fjs->fjs_current; + fjs->fjs_current = NULL; + stats->fjss_uniques++; + continue; + } + + findjsobjects_free(fjs->fjs_current); + fjs->fjs_current = NULL; + + inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP); + inst->fjsi_addr = addr; + inst->fjsi_next = obj->fjso_instances.fjsi_next; + obj->fjso_instances.fjsi_next = inst; + obj->fjso_ninstances++; + } + + mdb_free(range, size); + + return (0); +} + +static int +findjsobjects_mapping(findjsobjects_state_t *fjs, const prmap_t *pmp, + const char *name) +{ + if (name != NULL && !(fjs->fjs_brk && (pmp->pr_mflags & MA_BREAK))) + return (0); + + if (fjs->fjs_addr != NULL && (fjs->fjs_addr < pmp->pr_vaddr || + fjs->fjs_addr >= pmp->pr_vaddr + pmp->pr_size)) + return (0); + + return (findjsobjects_range(fjs, pmp->pr_vaddr, pmp->pr_size)); +} + +static void +findjsobjects_references_add(findjsobjects_state_t *fjs, uintptr_t val, + const char *desc, size_t index) +{ + findjsobjects_referent_t search, *referent; + findjsobjects_reference_t *reference; + + search.fjsr_addr = val; + + if ((referent = avl_find(&fjs->fjs_referents, &search, NULL)) == NULL) + return; + + reference = mdb_zalloc(sizeof (*reference), UM_SLEEP | UM_GC); + reference->fjsrf_addr = fjs->fjs_addr; + + if (desc != NULL) { + reference->fjsrf_desc = + mdb_alloc(strlen(desc) + 1, UM_SLEEP | UM_GC); + (void) strcpy(reference->fjsrf_desc, desc); + } else { + reference->fjsrf_index = index; + } + + if (referent->fjsr_head == NULL) { + referent->fjsr_head = reference; + } else { + referent->fjsr_tail->fjsrf_next = reference; + } + + referent->fjsr_tail = reference; +} + +static int +findjsobjects_references_prop(const char *desc, uintptr_t val, void *arg) +{ + findjsobjects_references_add(arg, val, desc, -1); + + return (0); +} + +static void +findjsobjects_references_array(findjsobjects_state_t *fjs, + findjsobjects_obj_t *obj) +{ + findjsobjects_instance_t *inst = &obj->fjso_instances; + uintptr_t *elts; + size_t i, len; + + for (; inst != NULL; inst = inst->fjsi_next) { + uintptr_t addr = inst->fjsi_addr, ptr; + + if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 || + read_heap_array(ptr, &elts, &len, UM_SLEEP) != 0) + continue; + + fjs->fjs_addr = addr; + + for (i = 0; i < len; i++) + findjsobjects_references_add(fjs, elts[i], NULL, i); + + mdb_free(elts, len * sizeof (uintptr_t)); + } +} + +static void +findjsobjects_referent(findjsobjects_state_t *fjs, uintptr_t addr) +{ + findjsobjects_referent_t search, *referent; + + search.fjsr_addr = addr; + + if (avl_find(&fjs->fjs_referents, &search, NULL) != NULL) { + assert(fjs->fjs_marking); + mdb_warn("%p is already marked; ignoring\n", addr); + return; + } + + referent = mdb_zalloc(sizeof (findjsobjects_referent_t), UM_SLEEP); + referent->fjsr_addr = addr; + + avl_add(&fjs->fjs_referents, referent); + + if (fjs->fjs_tail != NULL) { + fjs->fjs_tail->fjsr_next = referent; + } else { + fjs->fjs_head = referent; + } + + fjs->fjs_tail = referent; + + if (fjs->fjs_marking) + mdb_printf("findjsobjects: marked %p\n", addr); +} + +static void +findjsobjects_references(findjsobjects_state_t *fjs) +{ + findjsobjects_reference_t *reference; + findjsobjects_referent_t *referent; + avl_tree_t *referents = &fjs->fjs_referents; + findjsobjects_obj_t *obj; + void *cookie = NULL; + uintptr_t addr; + + fjs->fjs_referred = B_FALSE; + + v8_silent++; + + /* + * First traverse over all objects and arrays, looking for references + * to our designated referent(s). + */ + for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) { + findjsobjects_instance_t *head = &obj->fjso_instances, *inst; + + if (obj->fjso_nprops != 0 && obj->fjso_props == NULL) { + findjsobjects_references_array(fjs, obj); + continue; + } + + for (inst = head; inst != NULL; inst = inst->fjsi_next) { + fjs->fjs_addr = inst->fjsi_addr; + + (void) jsobj_properties(inst->fjsi_addr, + findjsobjects_references_prop, fjs, NULL); + } + } + + v8_silent--; + fjs->fjs_addr = NULL; + + /* + * Now go over our referent(s), reporting any references that we have + * accumulated. + */ + for (referent = fjs->fjs_head; referent != NULL; + referent = referent->fjsr_next) { + addr = referent->fjsr_addr; + + if ((reference = referent->fjsr_head) == NULL) { + mdb_printf("%p is not referred to by a " + "known object.\n", addr); + continue; + } + + for (; reference != NULL; reference = reference->fjsrf_next) { + mdb_printf("%p referred to by %p", + addr, reference->fjsrf_addr); + + if (reference->fjsrf_desc == NULL) { + mdb_printf("[%d]\n", reference->fjsrf_index); + } else { + mdb_printf(".%s\n", reference->fjsrf_desc); + } + } + } + + /* + * Finally, destroy our referent nodes. + */ + while ((referent = avl_destroy_nodes(referents, &cookie)) != NULL) + mdb_free(referent, sizeof (findjsobjects_referent_t)); + + fjs->fjs_head = NULL; + fjs->fjs_tail = NULL; +} + +static findjsobjects_instance_t * +findjsobjects_instance(findjsobjects_state_t *fjs, uintptr_t addr, + findjsobjects_instance_t **headp) +{ + findjsobjects_obj_t *obj; + + for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) { + findjsobjects_instance_t *head = &obj->fjso_instances, *inst; + + for (inst = head; inst != NULL; inst = inst->fjsi_next) { + if (inst->fjsi_addr == addr) { + *headp = head; + return (inst); + } + } + } + + return (NULL); +} + +/*ARGSUSED*/ +static void +findjsobjects_match_all(findjsobjects_obj_t *obj, const char *ignored) +{ + mdb_printf("%p\n", obj->fjso_instances.fjsi_addr); +} + +static void +findjsobjects_match_propname(findjsobjects_obj_t *obj, const char *propname) +{ + findjsobjects_prop_t *prop; + + for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) { + if (strcmp(prop->fjsp_desc, propname) == 0) { + mdb_printf("%p\n", obj->fjso_instances.fjsi_addr); + return; + } + } +} + +static void +findjsobjects_match_constructor(findjsobjects_obj_t *obj, + const char *constructor) +{ + if (strcmp(constructor, obj->fjso_constructor) == 0) + mdb_printf("%p\n", obj->fjso_instances.fjsi_addr); +} + +static void +findjsobjects_match_kind(findjsobjects_obj_t *obj, const char *propkind) +{ + jspropinfo_t p = obj->fjso_propinfo; + + if (((p & JPI_NUMERIC) != 0 && strstr(propkind, "numeric") != NULL) || + ((p & JPI_DICT) != 0 && strstr(propkind, "dict") != NULL) || + ((p & JPI_INOBJECT) != 0 && strstr(propkind, "inobject") != NULL) || + ((p & JPI_PROPS) != 0 && strstr(propkind, "props") != NULL) || + ((p & JPI_HASTRANSITIONS) != 0 && + strstr(propkind, "transitions") != NULL) || + ((p & JPI_HASCONTENT) != 0 && + strstr(propkind, "content") != NULL) || + ((p & JPI_SKIPPED) != 0 && strstr(propkind, "skipped") != NULL) || + ((p & JPI_BADLAYOUT) != 0 && + strstr(propkind, "badlayout") != NULL)) { + mdb_printf("%p\n", obj->fjso_instances.fjsi_addr); + } +} + +static int +findjsobjects_match(findjsobjects_state_t *fjs, uintptr_t addr, + uint_t flags, void (*func)(findjsobjects_obj_t *, const char *), + const char *match) +{ + findjsobjects_obj_t *obj; + + if (!(flags & DCMD_ADDRSPEC)) { + for (obj = fjs->fjs_objects; obj != NULL; + obj = obj->fjso_next) { + if (obj->fjso_malformed && !fjs->fjs_allobjs) + continue; + + func(obj, match); + } + + return (DCMD_OK); + } + + /* + * First, look for the specified address among the representative + * objects. + */ + for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) { + if (obj->fjso_instances.fjsi_addr == addr) { + func(obj, match); + return (DCMD_OK); + } + } + + /* + * We didn't find it among the representative objects; iterate over + * all objects. + */ + for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) { + findjsobjects_instance_t *head = &obj->fjso_instances, *inst; + + for (inst = head; inst != NULL; inst = inst->fjsi_next) { + if (inst->fjsi_addr == addr) { + func(obj, match); + return (DCMD_OK); + } + } + } + + mdb_warn("%p does not correspond to a known object\n", addr); + return (DCMD_ERR); +} + +static void +findjsobjects_print(findjsobjects_obj_t *obj) +{ + int col = 19 + (sizeof (uintptr_t) * 2) + strlen("..."), len; + uintptr_t addr = obj->fjso_instances.fjsi_addr; + findjsobjects_prop_t *prop; + + mdb_printf("%?p %8d %8d ", + addr, obj->fjso_ninstances, obj->fjso_nprops); + + if (obj->fjso_constructor[0] != '\0') { + mdb_printf("%s%s", obj->fjso_constructor, + obj->fjso_props != NULL ? ": " : ""); + col += strlen(obj->fjso_constructor) + 2; + } + + for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) { + if (col + (len = strlen(prop->fjsp_desc) + 2) < 80) { + mdb_printf("%s%s", prop->fjsp_desc, + prop->fjsp_next != NULL ? ", " : ""); + col += len; + } else { + mdb_printf("..."); + break; + } + } + + mdb_printf("\n", col); +} + +static void +dcmd_findjsobjects_help(void) +{ + mdb_printf("%s\n\n", +"Finds all JavaScript objects in the V8 heap via brute force iteration over\n" +"all mapped anonymous memory. (This can take up to several minutes on large\n" +"dumps.) The output consists of representative objects, the number of\n" +"instances of that object and the number of properties on the object --\n" +"followed by the constructor and first few properties of the objects. Once\n" +"run, subsequent calls to ::findjsobjects use cached data. If provided an\n" +"address (and in the absence of -r, described below), ::findjsobjects treats\n" +"the address as that of a representative object, and lists all instances of\n" +"that object (that is, all objects that have a matching property signature)."); + + mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + mdb_inc_indent(2); + + mdb_printf("%s\n", +" -b Include the heap denoted by the brk(2) (normally excluded)\n" +" -c cons Display representative objects with the specified constructor\n" +" -p prop Display representative objects that have the specified property\n" +" -l List all objects that match the representative object\n" +" -m Mark specified object for later reference determination via -r\n" +" -r Find references to the specified and/or marked object(s)\n" +" -v Provide verbose statistics\n"); +} + +static findjsobjects_state_t findjsobjects_state; + +static int +findjsobjects_run(findjsobjects_state_t *fjs) +{ + struct ps_prochandle *Pr; + findjsobjects_obj_t *obj; + findjsobjects_stats_t *stats = &fjs->fjs_stats; + + if (!fjs->fjs_initialized) { + avl_create(&fjs->fjs_tree, + (int(*)(const void *, const void *))findjsobjects_cmp, + sizeof (findjsobjects_obj_t), + offsetof(findjsobjects_obj_t, fjso_node)); + + avl_create(&fjs->fjs_referents, + (int(*)(const void *, const void *)) + findjsobjects_cmp_referents, + sizeof (findjsobjects_referent_t), + offsetof(findjsobjects_referent_t, fjsr_node)); + + avl_create(&fjs->fjs_funcinfo, + (int(*)(const void *, const void*)) + findjsobjects_cmp_funcinfo, + sizeof (findjsobjects_func_t), + offsetof(findjsobjects_func_t, fjsf_node)); + + fjs->fjs_initialized = B_TRUE; + } + + if (avl_is_empty(&fjs->fjs_tree)) { + findjsobjects_obj_t **sorted; + int nobjs, i; + hrtime_t start = gethrtime(); + + if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) { + mdb_warn("couldn't read pshandle xdata"); + return (-1); + } + + v8_silent++; + + if (Pmapping_iter(Pr, + (proc_map_f *)findjsobjects_mapping, fjs) != 0) { + v8_silent--; + return (-1); + } + + if ((nobjs = avl_numnodes(&fjs->fjs_tree)) != 0) { + /* + * We have the objects -- now sort them. + */ + sorted = mdb_alloc(nobjs * sizeof (void *), + UM_SLEEP | UM_GC); + + for (obj = fjs->fjs_objects, i = 0; obj != NULL; + obj = obj->fjso_next, i++) { + sorted[i] = obj; + } + + qsort(sorted, avl_numnodes(&fjs->fjs_tree), + sizeof (void *), findjsobjects_cmp_ninstances); + + for (i = 1, fjs->fjs_objects = sorted[0]; + i < nobjs; i++) + sorted[i - 1]->fjso_next = sorted[i]; + + sorted[nobjs - 1]->fjso_next = NULL; + } + + v8_silent--; + + if (fjs->fjs_verbose) { + const char *f = "findjsobjects: %30s => %d\n"; + int elapsed = (int)((gethrtime() - start) / NANOSEC); + + mdb_printf(f, "elapsed time (seconds)", elapsed); + mdb_printf(f, "heap objects", stats->fjss_heapobjs); + mdb_printf(f, "type reads", stats->fjss_typereads); + mdb_printf(f, "cached reads", stats->fjss_cached); + mdb_printf(f, "JavaScript objects", stats->fjss_jsobjs); + mdb_printf(f, "processed objects", stats->fjss_objects); + mdb_printf(f, "processed arrays", stats->fjss_arrays); + mdb_printf(f, "unique objects", stats->fjss_uniques); + mdb_printf(f, "functions found", stats->fjss_funcs); + mdb_printf(f, "unique functions", + stats->fjss_funcs_unique); + mdb_printf(f, "functions skipped", + stats->fjss_funcs_skipped); + } + } + + return (0); +} + +static int +dcmd_findjsobjects(uintptr_t addr, + uint_t flags, int argc, const mdb_arg_t *argv) +{ + findjsobjects_state_t *fjs = &findjsobjects_state; + findjsobjects_obj_t *obj; + boolean_t references = B_FALSE, listlike = B_FALSE; + const char *propname = NULL; + const char *constructor = NULL; + const char *propkind = NULL; + + fjs->fjs_verbose = B_FALSE; + fjs->fjs_brk = B_FALSE; + fjs->fjs_marking = B_FALSE; + fjs->fjs_allobjs = B_FALSE; + + if (mdb_getopts(argc, argv, + 'a', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_allobjs, + 'b', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_brk, + 'c', MDB_OPT_STR, &constructor, + 'k', MDB_OPT_STR, &propkind, + 'l', MDB_OPT_SETBITS, B_TRUE, &listlike, + 'm', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_marking, + 'p', MDB_OPT_STR, &propname, + 'r', MDB_OPT_SETBITS, B_TRUE, &references, + 'v', MDB_OPT_SETBITS, B_TRUE, &fjs->fjs_verbose, + NULL) != argc) + return (DCMD_USAGE); + + if (findjsobjects_run(fjs) != 0) + return (DCMD_ERR); + + if (listlike && !(flags & DCMD_ADDRSPEC)) { + if (propname != NULL || constructor != NULL || + propkind != NULL) { + char opt = propname != NULL ? 'p' : + propkind != NULL ? 'k' :'c'; + + mdb_warn("cannot specify -l with -%c; instead, pipe " + "output of ::findjsobjects -%c to " + "::findjsobjects -l\n", opt, opt); + return (DCMD_ERR); + } + + return (findjsobjects_match(fjs, addr, flags, + findjsobjects_match_all, NULL)); + } + + if (propname != NULL) { + if (constructor != NULL || propkind != NULL) { + mdb_warn("cannot specify both a property name " + "and a %s\n", constructor != NULL ? + "constructor" : "property kind"); + return (DCMD_ERR); + } + + return (findjsobjects_match(fjs, addr, flags, + findjsobjects_match_propname, propname)); + } + + if (constructor != NULL) { + if (propkind != NULL) { + mdb_warn("cannot specify both a constructor name " + "and a property kind\n"); + return (DCMD_ERR); + } + + return (findjsobjects_match(fjs, addr, flags, + findjsobjects_match_constructor, constructor)); + } + + if (propkind != NULL) { + return (findjsobjects_match(fjs, addr, flags, + findjsobjects_match_kind, propkind)); + } + + if (references && !(flags & DCMD_ADDRSPEC) && + avl_is_empty(&fjs->fjs_referents)) { + mdb_warn("must specify or mark an object to find references\n"); + return (DCMD_ERR); + } + + if (fjs->fjs_marking && !(flags & DCMD_ADDRSPEC)) { + mdb_warn("must specify an object to mark\n"); + return (DCMD_ERR); + } + + if (references && fjs->fjs_marking) { + mdb_warn("can't both mark an object and find its references\n"); + return (DCMD_ERR); + } + + if (flags & DCMD_ADDRSPEC) { + findjsobjects_instance_t *inst, *head; + + /* + * If we've been passed an address, it's to either list like + * objects (-l), mark an object (-m) or find references to the + * specified/marked objects (-r). (Note that the absence of + * any of these options implies -l.) + */ + inst = findjsobjects_instance(fjs, addr, &head); + + if (inst == NULL) { + mdb_warn("%p is not a valid object\n", addr); + return (DCMD_ERR); + } + + if (!references && !fjs->fjs_marking) { + for (inst = head; inst != NULL; inst = inst->fjsi_next) + mdb_printf("%p\n", inst->fjsi_addr); + + return (DCMD_OK); + } + + if (!listlike) { + findjsobjects_referent(fjs, inst->fjsi_addr); + } else { + for (inst = head; inst != NULL; inst = inst->fjsi_next) + findjsobjects_referent(fjs, inst->fjsi_addr); + } + } + + if (references) + findjsobjects_references(fjs); + + if (references || fjs->fjs_marking) + return (DCMD_OK); + + mdb_printf("%?s %8s %8s %s\n", "OBJECT", + "#OBJECTS", "#PROPS", "CONSTRUCTOR: PROPS"); + + for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) { + if (obj->fjso_malformed && !fjs->fjs_allobjs) + continue; + + findjsobjects_print(obj); + } + + return (DCMD_OK); +} + +/* + * Given a Node Buffer object, print out details about it. With "-a", just + * print the address. + */ +/* ARGSUSED */ +static int +dcmd_nodebuffer(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + boolean_t opt_f = B_FALSE; + char buf[80]; + char *bufp = buf; + size_t len = sizeof (buf); + uintptr_t elts, rawbuf; + + /* + * The undocumented "-f" option allows users to override constructor + * checks. + */ + if (mdb_getopts(argc, argv, + 'f', MDB_OPT_SETBITS, B_TRUE, &opt_f, NULL) != argc) + return (DCMD_USAGE); + + if (!opt_f) { + if (obj_jsconstructor(addr, &bufp, &len, B_FALSE) != 0) + return (DCMD_ERR); + + if (strcmp(buf, "Buffer") != 0) { + mdb_warn("%p does not appear to be a buffer\n", addr); + return (DCMD_ERR); + } + } + + if (read_heap_ptr(&elts, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) + return (DCMD_ERR); + + if (obj_v8internal(elts, 0, &rawbuf) != 0) + return (DCMD_ERR); + + mdb_printf("%p\n", rawbuf); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jsconstructor(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + boolean_t opt_v = B_FALSE; + char buf[80]; + char *bufp; + size_t len = sizeof (buf); + + if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, + NULL) != argc) + return (DCMD_USAGE); + + bufp = buf; + if (obj_jsconstructor(addr, &bufp, &len, opt_v)) + return (DCMD_ERR); + + mdb_printf("%s\n", buf); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uintptr_t fptr, raddr; + boolean_t opt_i = B_FALSE; + jsframe_t jsf; + int rv; + + bzero(&jsf, sizeof (jsf)); + jsf.jsf_nlines = 5; + + if (mdb_getopts(argc, argv, + 'a', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_showall, + 'v', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_verbose, + 'i', MDB_OPT_SETBITS, B_TRUE, &opt_i, + 'f', MDB_OPT_STR, &jsf.jsf_func, + 'n', MDB_OPT_UINTPTR, &jsf.jsf_nlines, + 'p', MDB_OPT_STR, &jsf.jsf_prop, NULL) != argc) + return (DCMD_USAGE); + + /* + * As with $C, we assume we are given a *pointer* to the frame pointer + * for a frame, rather than the actual frame pointer for the frame of + * interest. This is needed to show the instruction pointer, which is + * actually stored with the next frame. For debugging, this can be + * overridden with the "-i" option (for "immediate"). + */ + if (opt_i) { + rv = do_jsframe(addr, 0, &jsf); + if (rv == 0) + jsframe_print_skipped(&jsf); + return (rv); + } + + if (mdb_vread(&raddr, sizeof (raddr), + addr + sizeof (uintptr_t)) == -1) { + mdb_warn("failed to read return address from %p", + addr + sizeof (uintptr_t)); + return (DCMD_ERR); + } + + if (mdb_vread(&fptr, sizeof (fptr), addr) == -1) { + mdb_warn("failed to read frame pointer from %p", addr); + return (DCMD_ERR); + } + + if (fptr == NULL) + return (DCMD_OK); + + rv = do_jsframe(fptr, raddr, &jsf); + if (rv == 0) + jsframe_print_skipped(&jsf); + return (rv); +} + +static void +jsobj_print_propinfo(jspropinfo_t propinfo) +{ + if (propinfo == JPI_NONE) + return; + + mdb_printf("property kind: "); + if ((propinfo & JPI_NUMERIC) != 0) + mdb_printf("numeric-named "); + if ((propinfo & JPI_DICT) != 0) + mdb_printf("dictionary "); + if ((propinfo & JPI_INOBJECT) != 0) + mdb_printf("in-object "); + if ((propinfo & JPI_PROPS) != 0) + mdb_printf("\"properties\" array "); + mdb_printf("\n"); + + if ((propinfo & (JPI_HASTRANSITIONS | JPI_HASCONTENT)) != 0) { + mdb_printf("fallbacks: "); + if ((propinfo & JPI_HASTRANSITIONS) != 0) + mdb_printf("transitions "); + if ((propinfo & JPI_HASCONTENT) != 0) + mdb_printf("content "); + mdb_printf("\n"); + } + + if ((propinfo & JPI_SKIPPED) != 0) + mdb_printf( + "some properties skipped due to unexpected layout\n"); + if ((propinfo & JPI_BADLAYOUT) != 0) + mdb_printf("object has unexpected layout\n"); +} + +/* ARGSUSED */ +static int +dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + char *buf, *bufp; + size_t bufsz = 262144, len = bufsz; + jsobj_print_t jsop; + boolean_t opt_b = B_FALSE; + boolean_t opt_v = B_FALSE; + int rv, i; + + bzero(&jsop, sizeof (jsop)); + jsop.jsop_depth = 2; + jsop.jsop_printaddr = B_FALSE; + + i = mdb_getopts(argc, argv, + 'a', MDB_OPT_SETBITS, B_TRUE, &jsop.jsop_printaddr, + 'b', MDB_OPT_SETBITS, B_TRUE, &opt_b, + 'd', MDB_OPT_UINT64, &jsop.jsop_depth, + 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, NULL); + + if (opt_b) + jsop.jsop_baseaddr = addr; + + do { + if (i != argc) { + const mdb_arg_t *member = &argv[i++]; + + if (member->a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + jsop.jsop_member = member->a_un.a_str; + } + + for (;;) { + if ((buf = bufp = + mdb_zalloc(bufsz, UM_NOSLEEP)) == NULL) + return (DCMD_ERR); + + jsop.jsop_bufp = &bufp; + jsop.jsop_lenp = &len; + + rv = jsobj_print(addr, &jsop); + + if (len > 0) + break; + + mdb_free(buf, bufsz); + bufsz <<= 1; + len = bufsz; + } + + if (jsop.jsop_member == NULL && rv != 0) { + if (!jsop.jsop_descended) + mdb_warn("%s\n", buf); + + return (DCMD_ERR); + } + + if (jsop.jsop_member && !jsop.jsop_found) { + if (jsop.jsop_baseaddr) + (void) mdb_printf("%p: ", jsop.jsop_baseaddr); + + (void) mdb_printf("undefined%s", + i < argc ? " " : ""); + } else { + (void) mdb_printf("%s%s", buf, i < argc && + !isspace(buf[strlen(buf) - 1]) ? " " : ""); + } + + mdb_free(buf, bufsz); + jsop.jsop_found = B_FALSE; + jsop.jsop_baseaddr = NULL; + } while (i < argc); + + mdb_printf("\n"); + + if (opt_v) + jsobj_print_propinfo(jsop.jsop_propinfo); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jssource(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + const char *typename; + uintptr_t nlines = 5; + uintptr_t funcinfop, scriptp, funcnamep; + uintptr_t tokpos, endpos; + uint8_t type; + char buf[256]; + char *bufp = buf; + size_t len = sizeof (buf); + + if (mdb_getopts(argc, argv, 'n', MDB_OPT_UINTPTR, &nlines, + NULL) != argc) + return (DCMD_USAGE); + + if (!V8_IS_HEAPOBJECT(addr) || read_typebyte(&type, addr) != 0) { + mdb_warn("%p is not a heap object\n", addr); + return (DCMD_ERR); + } + + typename = enum_lookup_str(v8_types, type, ""); + if (strcmp(typename, "JSFunction") != 0) { + mdb_warn("%p is not a JSFunction\n", addr); + return (DCMD_ERR); + } + + if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 || + read_heap_ptr(&scriptp, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&funcnamep, scriptp, V8_OFF_SCRIPT_NAME) != 0) { + mdb_warn("%p: failed to find script for function\n", addr); + return (DCMD_ERR); + } + + if (read_heap_maybesmi(&tokpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 || + read_heap_maybesmi(&endpos, funcinfop, + V8_OFF_SHAREDFUNCTIONINFO_END_POSITION) != 0) { + mdb_warn("%p: failed to find function's boundaries\n", addr); + } + + if (jsstr_print(funcnamep, JSSTR_NUDE, &bufp, &len) == 0) + mdb_printf("file: %s\n", buf); + + if (tokpos != endpos) + jsfunc_lines(scriptp, tokpos, endpos, nlines, "%5d "); + mdb_printf("\n"); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jsfunctions(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + findjsobjects_state_t *fjs = &findjsobjects_state; + findjsobjects_func_t *func; + uintptr_t funcinfo; + boolean_t showrange = B_FALSE; + const char *name = NULL, *filename = NULL; + uintptr_t instr = 0; + + if (mdb_getopts(argc, argv, + 'x', MDB_OPT_UINTPTR, &instr, + 'X', MDB_OPT_SETBITS, B_TRUE, &showrange, + 'n', MDB_OPT_STR, &name, + 's', MDB_OPT_STR, &filename, + NULL) != argc) + return (DCMD_USAGE); + + if (findjsobjects_run(fjs) != 0) + return (DCMD_ERR); + + if (!showrange) + mdb_printf("%?s %8s %-40s %s\n", "FUNC", "#FUNCS", "NAME", + "FROM"); + else + mdb_printf("%?s %8s %?s %?s %-40s %s\n", "FUNC", "#FUNCS", + "START", "END", "NAME", "FROM"); + + for (func = fjs->fjs_funcs; func != NULL; func = func->fjsf_next) { + uintptr_t code, ilen; + + funcinfo = func->fjsf_shared; + + if (func->fjsf_location[0] == '\0') { + uintptr_t tokpos, script, lends; + ptrdiff_t tokposoff = + V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION; + + /* + * We don't want to actually decode the token position + * as an SMI here, so we re-encode it when we pass it to + * jsfunc_lineno() below. + */ + if (read_heap_maybesmi(&tokpos, funcinfo, + tokposoff) != 0 || + read_heap_ptr(&script, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 || + read_heap_ptr(&lends, script, + V8_OFF_SCRIPT_LINE_ENDS) != 0 || + jsfunc_lineno(lends, V8_VALUE_SMI(tokpos), + func->fjsf_location, + sizeof (func->fjsf_location), NULL) != 0) { + func->fjsf_location[0] = '\0'; + } + } + + if (name != NULL && strstr(func->fjsf_funcname, name) == NULL) + continue; + + if (filename != NULL && + strstr(func->fjsf_scriptname, filename) == NULL) + continue; + + code = 0; + ilen = 0; + if ((showrange || instr != 0) && + (read_heap_ptr(&code, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0 || + read_heap_ptr(&ilen, code, + V8_OFF_CODE_INSTRUCTION_SIZE) != 0)) { + code = 0; + ilen = 0; + } + + if ((instr != 0 && ilen != 0) && + (instr < code + V8_OFF_CODE_INSTRUCTION_START || + instr >= code + V8_OFF_CODE_INSTRUCTION_START + ilen)) + continue; + + if (!showrange) { + mdb_printf("%?p %8d %-40s %s %s\n", + func->fjsf_instances.fjsi_addr, + func->fjsf_ninstances, func->fjsf_funcname, + func->fjsf_scriptname, func->fjsf_location); + } else { + uintptr_t code, ilen; + + if (read_heap_ptr(&code, funcinfo, + V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0 || + read_heap_ptr(&ilen, code, + V8_OFF_CODE_INSTRUCTION_SIZE) != 0) { + mdb_printf("%?p %8d %?s %?s %-40s %s %s\n", + func->fjsf_instances.fjsi_addr, + func->fjsf_ninstances, "?", "?", + func->fjsf_funcname, func->fjsf_scriptname, + func->fjsf_location); + } else { + mdb_printf("%?p %8d %?p %?p %-40s %s %s\n", + func->fjsf_instances.fjsi_addr, + func->fjsf_ninstances, + code + V8_OFF_CODE_INSTRUCTION_START, + code + V8_OFF_CODE_INSTRUCTION_START + ilen, + func->fjsf_funcname, func->fjsf_scriptname, + func->fjsf_location); + } + } + } + + return (DCMD_OK); +} + +static void +dcmd_jsfunctions_help(void) +{ + mdb_printf("%s\n\n", +"Lists JavaScript functions, optionally filtered by a substring of the\n" +"function name or script filename or by the instruction address. This uses\n" +"the cache created by ::findjsobjects. If ::findjsobjects has not already\n" +"been run, this command runs it automatically without printing the output.\n" +"This can take anywhere from a second to several minutes, depending on the\n" +"size of the core dump.\n" +"\n" +"It's important to keep in mind that each time you create a function in\n" +"JavaScript (even from a function definition that has already been used),\n" +"the VM must create a new object to represent it. For example, if your\n" +"program has a function A that returns a closure B, the VM will create new\n" +"instances of the closure function (B) each time the surrounding function (A)\n" +"is called. To show this, the output of this command consists of one line \n" +"per function definition that appears in the JavaScript source, and the\n" +"\"#FUNCS\" column shows how many different functions were created by VM from\n" +"this definition."); + + mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + mdb_inc_indent(2); + + mdb_printf("%s\n", +" -f file List functions that were defined in a file whose name contains\n" +" this substring.\n" +" -n func List functions whose name contains this substring\n" +" -x instr List functions whose compiled instructions include this address\n" +" -X Show where the function's instructions are stored in memory\n"); +} + +/* ARGSUSED */ +static int +dcmd_v8field(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + v8_class_t *clp; + v8_field_t *flp; + const char *klass, *field; + uintptr_t offset = 0; + + /* + * We may be invoked with either two arguments (class and field name) or + * three (an offset to save). + */ + if (argc != 2 && argc != 3) + return (DCMD_USAGE); + + if (argv[0].a_type != MDB_TYPE_STRING || + argv[1].a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + klass = argv[0].a_un.a_str; + field = argv[1].a_un.a_str; + + if (argc == 3) { + if (argv[2].a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + offset = mdb_strtoull(argv[2].a_un.a_str); + } + + for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) + if (strcmp(clp->v8c_name, klass) == 0) + break; + + if (clp == NULL) { + (void) mdb_printf("error: no such class: \"%s\"", klass); + return (DCMD_ERR); + } + + for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) + if (strcmp(field, flp->v8f_name) == 0) + break; + + if (flp == NULL) { + if (argc == 2) { + mdb_printf("error: no such field in class \"%s\": " + "\"%s\"", klass, field); + return (DCMD_ERR); + } + + flp = conf_field_create(clp, field, offset); + if (flp == NULL) { + mdb_warn("failed to create field"); + return (DCMD_ERR); + } + } else if (argc == 3) { + flp->v8f_offset = offset; + } + + mdb_printf("%s::%s at offset 0x%x\n", klass, field, flp->v8f_offset); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_v8array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uint8_t type; + uintptr_t *array; + size_t ii, len; + + if (read_typebyte(&type, addr) != 0) + return (DCMD_ERR); + + if (type != V8_TYPE_FIXEDARRAY) { + mdb_warn("%p is not an instance of FixedArray\n", addr); + return (DCMD_ERR); + } + + if (read_heap_array(addr, &array, &len, UM_SLEEP | UM_GC) != 0) + return (DCMD_ERR); + + for (ii = 0; ii < len; ii++) + mdb_printf("%p\n", array[ii]); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uintptr_t raddr; + jsframe_t jsf; + + bzero(&jsf, sizeof (jsf)); + jsf.jsf_nlines = 5; + + if (mdb_getopts(argc, argv, + 'a', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_showall, + 'v', MDB_OPT_SETBITS, B_TRUE, &jsf.jsf_verbose, + 'f', MDB_OPT_STR, &jsf.jsf_func, + 'n', MDB_OPT_UINTPTR, &jsf.jsf_nlines, + 'p', MDB_OPT_STR, &jsf.jsf_prop, + NULL) != argc) + return (DCMD_USAGE); + + /* + * The "::jsframe" walker iterates the valid frame pointers, but the + * "::jsframe" dcmd looks at the frame after the one it was given, so we + * have to explicitly examine the top frame here. + */ + if (!(flags & DCMD_ADDRSPEC)) { + if (load_current_context(&addr, &raddr) != 0 || + do_jsframe(addr, raddr, &jsf) != 0) + return (DCMD_ERR); + } + + if (mdb_pwalk_dcmd("jsframe", "jsframe", argc, argv, addr) == -1) + return (DCMD_ERR); + + jsframe_print_skipped(&jsf); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_v8str(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + boolean_t opt_v = B_FALSE; + char buf[512 * 1024]; + char *bufp; + size_t len; + + if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v, + NULL) != argc) + return (DCMD_USAGE); + + bufp = buf; + len = sizeof (buf); + if (jsstr_print(addr, (opt_v ? JSSTR_VERBOSE : JSSTR_NONE) | + JSSTR_QUOTED, &bufp, &len) != 0) + return (DCMD_ERR); + + mdb_printf("%s\n", buf); + return (DCMD_OK); +} + +static void +dcmd_v8load_help(void) +{ + v8_cfg_t *cfp, **cfgpp; + + mdb_printf( + "To traverse in-memory V8 structures, the V8 dmod requires\n" + "configuration that describes the layout of various V8 structures\n" + "in memory. Normally, this information is pulled from metadata\n" + "in the target binary. However, it's possible to use the module\n" + "with a binary not built with metadata by loading one of the\n" + "canned configurations.\n\n"); + + mdb_printf("Available configurations:\n"); + + (void) mdb_inc_indent(4); + + for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) { + cfp = *cfgpp; + mdb_printf("%-10s %s\n", cfp->v8cfg_name, cfp->v8cfg_label); + } + + (void) mdb_dec_indent(4); +} + +/* ARGSUSED */ +static int +dcmd_v8load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + v8_cfg_t *cfgp = NULL, **cfgpp; + + if (v8_classes != NULL) { + mdb_warn("v8 module already configured\n"); + return (DCMD_ERR); + } + + if (argc < 1 || argv->a_type != MDB_TYPE_STRING) + return (DCMD_USAGE); + + for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) { + cfgp = *cfgpp; + if (strcmp(argv->a_un.a_str, cfgp->v8cfg_name) == 0) + break; + } + + if (cfgp == NULL || cfgp->v8cfg_name == NULL) { + mdb_warn("unknown configuration: \"%s\"\n", argv->a_un.a_str); + return (DCMD_ERR); + } + + if (autoconfigure(cfgp) == -1) { + mdb_warn("autoconfigure failed\n"); + return (DCMD_ERR); + } + + mdb_printf("V8 dmod configured based on %s\n", cfgp->v8cfg_name); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +dcmd_v8warnings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + v8_warnings ^= 1; + mdb_printf("v8 warnings are now %s\n", v8_warnings ? "on" : "off"); + + return (DCMD_OK); +} + +static int +walk_jsframes_init(mdb_walk_state_t *wsp) +{ + if (wsp->walk_addr != NULL) + return (WALK_NEXT); + + if (load_current_context(&wsp->walk_addr, NULL) != 0) + return (WALK_ERR); + + return (WALK_NEXT); +} + +static int +walk_jsframes_step(mdb_walk_state_t *wsp) +{ + uintptr_t addr, next; + int rv; + + addr = wsp->walk_addr; + rv = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata); + + if (rv != WALK_NEXT) + return (rv); + + if (mdb_vread(&next, sizeof (next), addr) == -1) + return (WALK_ERR); + + if (next == NULL) + return (WALK_DONE); + + wsp->walk_addr = next; + return (WALK_NEXT); +} + +typedef struct jsprop_walk_data { + int jspw_nprops; + int jspw_current; + uintptr_t *jspw_props; +} jsprop_walk_data_t; + +/*ARGSUSED*/ +static int +walk_jsprop_nprops(const char *desc, uintptr_t val, void *arg) +{ + jsprop_walk_data_t *jspw = arg; + jspw->jspw_nprops++; + + return (0); +} + +/*ARGSUSED*/ +static int +walk_jsprop_props(const char *desc, uintptr_t val, void *arg) +{ + jsprop_walk_data_t *jspw = arg; + jspw->jspw_props[jspw->jspw_current++] = val; + + return (0); +} + +static int +walk_jsprop_init(mdb_walk_state_t *wsp) +{ + jsprop_walk_data_t *jspw; + uintptr_t addr; + uint8_t type; + + if ((addr = wsp->walk_addr) == NULL) { + mdb_warn("'jsprop' does not support global walks\n"); + return (WALK_ERR); + } + + if (!V8_IS_HEAPOBJECT(addr) || read_typebyte(&type, addr) != 0 || + type != V8_TYPE_JSOBJECT) { + mdb_warn("%p is not a JSObject\n", addr); + return (WALK_ERR); + } + + jspw = mdb_zalloc(sizeof (jsprop_walk_data_t), UM_SLEEP | UM_GC); + + if (jsobj_properties(addr, walk_jsprop_nprops, jspw, NULL) == -1) { + mdb_warn("couldn't iterate over properties for %p\n", addr); + return (WALK_ERR); + } + + jspw->jspw_props = mdb_zalloc(jspw->jspw_nprops * + sizeof (uintptr_t), UM_SLEEP | UM_GC); + + if (jsobj_properties(addr, walk_jsprop_props, jspw, NULL) == -1) { + mdb_warn("couldn't iterate over properties for %p\n", addr); + return (WALK_ERR); + } + + jspw->jspw_current = 0; + wsp->walk_data = jspw; + + return (WALK_NEXT); +} + +static int +walk_jsprop_step(mdb_walk_state_t *wsp) +{ + jsprop_walk_data_t *jspw = wsp->walk_data; + int rv; + + if (jspw->jspw_current >= jspw->jspw_nprops) + return (WALK_DONE); + + if ((rv = wsp->walk_callback(jspw->jspw_props[jspw->jspw_current++], + NULL, wsp->walk_cbdata)) != WALK_NEXT) + return (rv); + + return (WALK_NEXT); +} + +/* + * MDB linkage + */ + +static const mdb_dcmd_t v8_mdb_dcmds[] = { + /* + * Commands to inspect Node-level state + */ + { "nodebuffer", ":[-a]", + "print details about the given Node Buffer", dcmd_nodebuffer }, + + /* + * Commands to inspect JavaScript-level state + */ + { "jsconstructor", ":[-v]", + "print the constructor for a JavaScript object", + dcmd_jsconstructor }, + { "jsframe", ":[-aiv] [-f function] [-p property] [-n numlines]", + "summarize a JavaScript stack frame", dcmd_jsframe }, + { "jsprint", ":[-ab] [-d depth] [member]", "print a JavaScript object", + dcmd_jsprint }, + { "jssource", ":[-n numlines]", + "print the source code for a JavaScript function", + dcmd_jssource }, + { "jsstack", "[-av] [-f function] [-p property] [-n numlines]", + "print a JavaScript stacktrace", dcmd_jsstack }, + { "findjsobjects", "?[-vb] [-r | -c cons | -p prop]", "find JavaScript " + "objects", dcmd_findjsobjects, dcmd_findjsobjects_help }, + { "jsfunctions", "[-X] [-s file_filter] [-n name_filter] " + "[-x instr_filter]", "list JavaScript functions", + dcmd_jsfunctions, dcmd_jsfunctions_help }, + + /* + * Commands to inspect V8-level state + */ + { "v8array", ":", "print elements of a V8 FixedArray", + dcmd_v8array }, + { "v8classes", NULL, "list known V8 heap object C++ classes", + dcmd_v8classes }, + { "v8code", ":[-d]", "print information about a V8 Code object", + dcmd_v8code }, + { "v8field", "classname fieldname offset", + "manually add a field to a given class", dcmd_v8field }, + { "v8function", ":[-d]", "print JSFunction object details", + dcmd_v8function }, + { "v8internal", ":[fieldidx]", "print v8 object internal fields", + dcmd_v8internal }, + { "v8load", "version", "load canned config for a specific V8 version", + dcmd_v8load, dcmd_v8load_help }, + { "v8frametypes", NULL, "list known V8 frame types", + dcmd_v8frametypes }, + { "v8print", ":[class]", "print a V8 heap object", + dcmd_v8print, dcmd_v8print_help }, + { "v8str", ":[-v]", "print the contents of a V8 string", + dcmd_v8str }, + { "v8type", ":", "print the type of a V8 heap object", + dcmd_v8type }, + { "v8types", NULL, "list known V8 heap object types", + dcmd_v8types }, + { "v8warnings", NULL, "toggle V8 warnings", + dcmd_v8warnings }, + + { NULL } +}; + +static const mdb_walker_t v8_mdb_walkers[] = { + { "jsframe", "walk V8 JavaScript stack frames", + walk_jsframes_init, walk_jsframes_step }, + { "jsprop", "walk property values for an object", + walk_jsprop_init, walk_jsprop_step }, + { NULL } +}; + +static mdb_modinfo_t v8_mdb = { MDB_API_VERSION, v8_mdb_dcmds, v8_mdb_walkers }; + +static void +configure(void) +{ + char *success; + v8_cfg_t *cfgp = NULL; + GElf_Sym sym; + int major, minor, build, patch; + + if (mdb_readsym(&major, sizeof (major), + "_ZN2v88internal7Version6major_E") == -1 || + mdb_readsym(&minor, sizeof (minor), + "_ZN2v88internal7Version6minor_E") == -1 || + mdb_readsym(&build, sizeof (build), + "_ZN2v88internal7Version6build_E") == -1 || + mdb_readsym(&patch, sizeof (patch), + "_ZN2v88internal7Version6patch_E") == -1) { + mdb_warn("failed to determine V8 version"); + return; + } + + v8_major = major; + v8_minor = minor; + v8_build = build; + v8_patch = patch; + mdb_printf("V8 version: %d.%d.%d.%d\n", + v8_major, v8_minor, v8_build, v8_patch); + + /* + * First look for debug metadata embedded within the binary, which may + * be present in recent V8 versions built with postmortem metadata. + */ + if (mdb_lookup_by_name("v8dbg_SmiTag", &sym) == 0) { + cfgp = &v8_cfg_target; + success = "Autoconfigured V8 support from target"; + } else if (v8_major == 3 && v8_minor == 1 && v8_build == 8) { + cfgp = &v8_cfg_04; + success = "Configured V8 support based on node v0.4"; + } else if (v8_major == 3 && v8_minor == 6 && v8_build == 6) { + cfgp = &v8_cfg_06; + success = "Configured V8 support based on node v0.6"; + } else { + mdb_printf("mdb_v8: target has no debug metadata and " + "no existing config found\n"); + return; + } + + if (autoconfigure(cfgp) != 0) { + mdb_warn("failed to autoconfigure from target; " + "commands may have incorrect results!\n"); + return; + } + + mdb_printf("%s\n", success); +} + +static void +enable_demangling(void) +{ + const char *symname = "_ZN2v88internal7Version6major_E"; + GElf_Sym sym; + char buf[64]; + + /* + * Try to determine whether C++ symbol demangling has been enabled. If + * not, enable it. + */ + if (mdb_lookup_by_name("_ZN2v88internal7Version6major_E", &sym) != 0) + return; + + (void) mdb_snprintf(buf, sizeof (buf), "%a", sym.st_value); + if (strstr(buf, symname) != NULL) + (void) mdb_eval("$G"); +} + +const mdb_modinfo_t * +_mdb_init(void) +{ + configure(); + enable_demangling(); + return (&v8_mdb); +} diff --git a/usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c b/usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c new file mode 100644 index 0000000000..d907242435 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c @@ -0,0 +1,728 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + +/* + * mdb_v8_cfg.c: canned configurations for previous V8 versions. + * + * The functions and data defined here enable this dmod to support debugging + * Node.js binaries that predated V8's built-in postmortem debugging support. + */ + +#include "v8cfg.h" + +/*ARGSUSED*/ +static int +v8cfg_target_iter(v8_cfg_t *cfgp, int (*func)(mdb_symbol_t *, void *), + void *arg) +{ + return (mdb_symbol_iter(MDB_OBJ_EVERY, MDB_DYNSYM, + MDB_BIND_GLOBAL | MDB_TYPE_OBJECT | MDB_TYPE_FUNC, + func, arg)); +} + +/*ARGSUSED*/ +static int +v8cfg_target_readsym(v8_cfg_t *cfgp, const char *name, intptr_t *valp) +{ + int val, rval; + + if ((rval = mdb_readsym(&val, sizeof (val), name)) != -1) + *valp = (intptr_t)val; + + return (rval); +} + +/* + * Analog of mdb_symbol_iter() for a canned configuration. + */ +static int +v8cfg_canned_iter(v8_cfg_t *cfgp, int (*func)(mdb_symbol_t *, void *), + void *arg) +{ + v8_cfg_symbol_t *v8sym; + mdb_symbol_t mdbsym; + int rv; + + for (v8sym = cfgp->v8cfg_symbols; v8sym->v8cs_name != NULL; v8sym++) { + mdbsym.sym_name = v8sym->v8cs_name; + mdbsym.sym_object = NULL; + mdbsym.sym_sym = NULL; + mdbsym.sym_table = 0; + mdbsym.sym_id = 0; + + if ((rv = func(&mdbsym, arg)) != 0) + return (rv); + } + + return (0); +} + +/* + * Analog of mdb_readsym() for a canned configuration. + */ +static int +v8cfg_canned_readsym(v8_cfg_t *cfgp, const char *name, intptr_t *valp) +{ + v8_cfg_symbol_t *v8sym; + + for (v8sym = cfgp->v8cfg_symbols; v8sym->v8cs_name != NULL; v8sym++) { + if (strcmp(name, v8sym->v8cs_name) == 0) + break; + } + + if (v8sym->v8cs_name == NULL) + return (-1); + + *valp = v8sym->v8cs_value; + return (0); +} + +/* + * Canned configuration for the V8 bundled with Node.js v0.4.8 and later. + */ +static v8_cfg_symbol_t v8_symbols_node_04[] = { + { "v8dbg_type_AccessCheckInfo__ACCESS_CHECK_INFO_TYPE", 0x91 }, + { "v8dbg_type_AccessorInfo__ACCESSOR_INFO_TYPE", 0x90 }, + { "v8dbg_type_BreakPointInfo__BREAK_POINT_INFO_TYPE", 0x9b }, + { "v8dbg_type_ByteArray__BYTE_ARRAY_TYPE", 0x86 }, + { "v8dbg_type_CallHandlerInfo__CALL_HANDLER_INFO_TYPE", 0x93 }, + { "v8dbg_type_Code__CODE_TYPE", 0x81 }, + { "v8dbg_type_CodeCache__CODE_CACHE_TYPE", 0x99 }, + { "v8dbg_type_ConsString__CONS_ASCII_STRING_TYPE", 0x5 }, + { "v8dbg_type_ConsString__CONS_ASCII_SYMBOL_TYPE", 0x45 }, + { "v8dbg_type_ConsString__CONS_STRING_TYPE", 0x1 }, + { "v8dbg_type_ConsString__CONS_SYMBOL_TYPE", 0x41 }, + { "v8dbg_type_DebugInfo__DEBUG_INFO_TYPE", 0x9a }, + { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_STRING_TYPE", 0x6 }, + { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_SYMBOL_TYPE", 0x46 }, + { "v8dbg_type_ExternalByteArray__EXTERNAL_BYTE_ARRAY_TYPE", 0x88 }, + { "v8dbg_type_ExternalFloatArray__EXTERNAL_FLOAT_ARRAY_TYPE", 0x8e }, + { "v8dbg_type_ExternalIntArray__EXTERNAL_INT_ARRAY_TYPE", 0x8c }, + { "v8dbg_type_ExternalShortArray__EXTERNAL_SHORT_ARRAY_TYPE", 0x8a }, + { "v8dbg_type_ExternalString__EXTERNAL_STRING_TYPE", 0x2 }, + { "v8dbg_type_ExternalString__EXTERNAL_SYMBOL_TYPE", 0x42 }, + { "v8dbg_type_ExternalUnsignedByteArray__" + "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", 0x89 }, + { "v8dbg_type_ExternalUnsignedIntArray__" + "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", 0x8d }, + { "v8dbg_type_ExternalUnsignedShortArray__" + "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", 0x8b }, + { "v8dbg_type_FixedArray__FIXED_ARRAY_TYPE", 0x9c }, + { "v8dbg_type_FunctionTemplateInfo__" + "FUNCTION_TEMPLATE_INFO_TYPE", 0x94 }, + { "v8dbg_type_HeapNumber__HEAP_NUMBER_TYPE", 0x84 }, + { "v8dbg_type_InterceptorInfo__INTERCEPTOR_INFO_TYPE", 0x92 }, + { "v8dbg_type_JSArray__JS_ARRAY_TYPE", 0xa5 }, + { "v8dbg_type_JSBuiltinsObject__JS_BUILTINS_OBJECT_TYPE", 0xa3 }, + { "v8dbg_type_JSFunction__JS_FUNCTION_TYPE", 0xa7 }, + { "v8dbg_type_JSGlobalObject__JS_GLOBAL_OBJECT_TYPE", 0xa2 }, + { "v8dbg_type_JSGlobalPropertyCell__" + "JS_GLOBAL_PROPERTY_CELL_TYPE", 0x83 }, + { "v8dbg_type_JSGlobalProxy__JS_GLOBAL_PROXY_TYPE", 0xa4 }, + { "v8dbg_type_JSMessageObject__JS_MESSAGE_OBJECT_TYPE", 0x9e }, + { "v8dbg_type_JSObject__JS_OBJECT_TYPE", 0xa0 }, + { "v8dbg_type_JSRegExp__JS_REGEXP_TYPE", 0xa6 }, + { "v8dbg_type_JSValue__JS_VALUE_TYPE", 0x9f }, + { "v8dbg_type_Map__MAP_TYPE", 0x80 }, + { "v8dbg_type_ObjectTemplateInfo__OBJECT_TEMPLATE_INFO_TYPE", 0x95 }, + { "v8dbg_type_Oddball__ODDBALL_TYPE", 0x82 }, + { "v8dbg_type_Script__SCRIPT_TYPE", 0x98 }, + { "v8dbg_type_SeqAsciiString__ASCII_STRING_TYPE", 0x4 }, + { "v8dbg_type_SeqAsciiString__ASCII_SYMBOL_TYPE", 0x44 }, + { "v8dbg_type_SharedFunctionInfo__SHARED_FUNCTION_INFO_TYPE", 0x9d }, + { "v8dbg_type_SignatureInfo__SIGNATURE_INFO_TYPE", 0x96 }, + { "v8dbg_type_String__STRING_TYPE", 0x0 }, + { "v8dbg_type_String__SYMBOL_TYPE", 0x40 }, + { "v8dbg_type_TypeSwitchInfo__TYPE_SWITCH_INFO_TYPE", 0x97 }, + + { "v8dbg_class_AccessCheckInfo__data__Object", 0xc }, + { "v8dbg_class_AccessCheckInfo__indexed_callback__Object", 0x8 }, + { "v8dbg_class_AccessCheckInfo__named_callback__Object", 0x4 }, + { "v8dbg_class_AccessorInfo__data__Object", 0xc }, + { "v8dbg_class_AccessorInfo__flag__Smi", 0x14 }, + { "v8dbg_class_AccessorInfo__getter__Object", 0x4 }, + { "v8dbg_class_AccessorInfo__name__Object", 0x10 }, + { "v8dbg_class_AccessorInfo__setter__Object", 0x8 }, + { "v8dbg_class_BreakPointInfo__break_point_objects__Object", 0x10 }, + { "v8dbg_class_BreakPointInfo__code_position__Smi", 0x4 }, + { "v8dbg_class_BreakPointInfo__source_position__Smi", 0x8 }, + { "v8dbg_class_BreakPointInfo__statement_position__Smi", 0xc }, + { "v8dbg_class_ByteArray__length__SMI", 0x4 }, + { "v8dbg_class_CallHandlerInfo__callback__Object", 0x4 }, + { "v8dbg_class_CallHandlerInfo__data__Object", 0x8 }, + { "v8dbg_class_Code__deoptimization_data__FixedArray", 0xc }, + { "v8dbg_class_Code__instruction_size__int", 0x4 }, + { "v8dbg_class_Code__instruction_start__int", 0x20 }, + { "v8dbg_class_Code__relocation_info__ByteArray", 0x8 }, + { "v8dbg_class_CodeCache__default_cache__FixedArray", 0x4 }, + { "v8dbg_class_CodeCache__normal_type_cache__Object", 0x8 }, + { "v8dbg_class_ConsString__first__String", 0xc }, + { "v8dbg_class_ConsString__second__String", 0x10 }, + { "v8dbg_class_DebugInfo__break_points__FixedArray", 0x14 }, + { "v8dbg_class_DebugInfo__code__Code", 0xc }, + { "v8dbg_class_DebugInfo__original_code__Code", 0x8 }, + { "v8dbg_class_DebugInfo__shared__SharedFunctionInfo", 0x4 }, + { "v8dbg_class_ExternalString__resource__Object", 0xc }, + { "v8dbg_class_FixedArray__data__uintptr_t", 0x8 }, + { "v8dbg_class_FixedArray__length__SMI", 0x4 }, + { "v8dbg_class_FunctionTemplateInfo__access_check_info__Object", 0x38 }, + { "v8dbg_class_FunctionTemplateInfo__call_code__Object", 0x10 }, + { "v8dbg_class_FunctionTemplateInfo__class_name__Object", 0x2c }, + { "v8dbg_class_FunctionTemplateInfo__flag__Smi", 0x3c }, + { "v8dbg_class_FunctionTemplateInfo__" + "indexed_property_handler__Object", 0x24 }, + { "v8dbg_class_FunctionTemplateInfo__" + "instance_call_handler__Object", 0x34 }, + { "v8dbg_class_FunctionTemplateInfo__instance_template__Object", 0x28 }, + { "v8dbg_class_FunctionTemplateInfo__" + "named_property_handler__Object", 0x20 }, + { "v8dbg_class_FunctionTemplateInfo__parent_template__Object", 0x1c }, + { "v8dbg_class_FunctionTemplateInfo__" + "property_accessors__Object", 0x14 }, + { "v8dbg_class_FunctionTemplateInfo__" + "prototype_template__Object", 0x18 }, + { "v8dbg_class_FunctionTemplateInfo__serial_number__Object", 0xc }, + { "v8dbg_class_FunctionTemplateInfo__signature__Object", 0x30 }, + { "v8dbg_class_GlobalObject__builtins__JSBuiltinsObject", 0xc }, + { "v8dbg_class_GlobalObject__global_context__Context", 0x10 }, + { "v8dbg_class_GlobalObject__global_receiver__JSObject", 0x14 }, + { "v8dbg_class_HeapNumber__value__SMI", 0x4 }, + { "v8dbg_class_HeapObject__map__Map", 0x0 }, + { "v8dbg_class_InterceptorInfo__data__Object", 0x18 }, + { "v8dbg_class_InterceptorInfo__deleter__Object", 0x10 }, + { "v8dbg_class_InterceptorInfo__enumerator__Object", 0x14 }, + { "v8dbg_class_InterceptorInfo__getter__Object", 0x4 }, + { "v8dbg_class_InterceptorInfo__query__Object", 0xc }, + { "v8dbg_class_InterceptorInfo__setter__Object", 0x8 }, + { "v8dbg_class_JSArray__length__Object", 0xc }, + { "v8dbg_class_JSFunction__literals__FixedArray", 0x1c }, + { "v8dbg_class_JSFunction__next_function_link__Object", 0x20 }, + { "v8dbg_class_JSFunction__prototype_or_initial_map__Object", 0x10 }, + { "v8dbg_class_JSFunction__shared__SharedFunctionInfo", 0x14 }, + { "v8dbg_class_JSGlobalProxy__context__Object", 0xc }, + { "v8dbg_class_JSMessageObject__arguments__JSArray", 0x10 }, + { "v8dbg_class_JSMessageObject__end_position__SMI", 0x24 }, + { "v8dbg_class_JSMessageObject__script__Object", 0x14 }, + { "v8dbg_class_JSMessageObject__stack_frames__Object", 0x1c }, + { "v8dbg_class_JSMessageObject__stack_trace__Object", 0x18 }, + { "v8dbg_class_JSMessageObject__start_position__SMI", 0x20 }, + { "v8dbg_class_JSMessageObject__type__String", 0xc }, + { "v8dbg_class_JSObject__elements__Object", 0x8 }, + { "v8dbg_class_JSObject__properties__FixedArray", 0x4 }, + { "v8dbg_class_JSRegExp__data__Object", 0xc }, + { "v8dbg_class_JSValue__value__Object", 0xc }, + { "v8dbg_class_Map__code_cache__Object", 0x18 }, + { "v8dbg_class_Map__constructor__Object", 0x10 }, + { "v8dbg_class_Map__inobject_properties__int", 0x5 }, + { "v8dbg_class_Map__instance_size__int", 0x4 }, + { "v8dbg_class_Map__instance_attributes__int", 0x8 }, + { "v8dbg_class_Map__instance_descriptors__DescriptorArray", 0x14 }, + { "v8dbg_class_ObjectTemplateInfo__constructor__Object", 0xc }, + { "v8dbg_class_ObjectTemplateInfo__" + "internal_field_count__Object", 0x10 }, + { "v8dbg_class_Oddball__to_number__Object", 0x8 }, + { "v8dbg_class_Oddball__to_string__String", 0x4 }, + { "v8dbg_class_Script__column_offset__Smi", 0x10 }, + { "v8dbg_class_Script__compilation_type__Smi", 0x24 }, + { "v8dbg_class_Script__context_data__Object", 0x18 }, + { "v8dbg_class_Script__data__Object", 0x14 }, + { "v8dbg_class_Script__eval_from_instructions_offset__Smi", 0x34 }, + { "v8dbg_class_Script__eval_from_shared__Object", 0x30 }, + { "v8dbg_class_Script__id__Object", 0x2c }, + { "v8dbg_class_Script__line_ends__Object", 0x28 }, + { "v8dbg_class_Script__line_offset__Smi", 0xc }, + { "v8dbg_class_Script__name__Object", 0x8 }, + { "v8dbg_class_Script__source__Object", 0x4 }, + { "v8dbg_class_Script__type__Smi", 0x20 }, + { "v8dbg_class_Script__wrapper__Proxy", 0x1c }, + { "v8dbg_class_SeqAsciiString__chars__char", 0xc }, + { "v8dbg_class_SharedFunctionInfo__code__Code", 0x8 }, + { "v8dbg_class_SharedFunctionInfo__compiler_hints__SMI", 0x50 }, + { "v8dbg_class_SharedFunctionInfo__construct_stub__Code", 0x10 }, + { "v8dbg_class_SharedFunctionInfo__debug_info__Object", 0x20 }, + { "v8dbg_class_SharedFunctionInfo__end_position__SMI", 0x48 }, + { "v8dbg_class_SharedFunctionInfo__" + "expected_nof_properties__SMI", 0x3c }, + { "v8dbg_class_SharedFunctionInfo__formal_parameter_count__SMI", 0x38 }, + { "v8dbg_class_SharedFunctionInfo__function_data__Object", 0x18 }, + { "v8dbg_class_SharedFunctionInfo__" + "function_token_position__SMI", 0x4c }, + { "v8dbg_class_SharedFunctionInfo__inferred_name__String", 0x24 }, + { "v8dbg_class_SharedFunctionInfo__initial_map__Object", 0x28 }, + { "v8dbg_class_SharedFunctionInfo__instance_class_name__Object", 0x14 }, + { "v8dbg_class_SharedFunctionInfo__length__SMI", 0x34 }, + { "v8dbg_class_SharedFunctionInfo__name__Object", 0x4 }, + { "v8dbg_class_SharedFunctionInfo__num_literals__SMI", 0x40 }, + { "v8dbg_class_SharedFunctionInfo__opt_count__SMI", 0x58 }, + { "v8dbg_class_SharedFunctionInfo__script__Object", 0x1c }, + { "v8dbg_class_SharedFunctionInfo__" + "start_position_and_type__SMI", 0x44 }, + { "v8dbg_class_SharedFunctionInfo__" + "this_property_assignments__Object", 0x2c }, + { "v8dbg_class_SharedFunctionInfo__" + "this_property_assignments_count__SMI", 0x54 }, + { "v8dbg_class_SignatureInfo__args__Object", 0x8 }, + { "v8dbg_class_SignatureInfo__receiver__Object", 0x4 }, + { "v8dbg_class_String__length__SMI", 0x4 }, + { "v8dbg_class_TemplateInfo__property_list__Object", 0x8 }, + { "v8dbg_class_TemplateInfo__tag__Object", 0x4 }, + { "v8dbg_class_TypeSwitchInfo__types__Object", 0x4 }, + + { "v8dbg_parent_AccessCheckInfo__Struct", 0x0 }, + { "v8dbg_parent_AccessorInfo__Struct", 0x0 }, + { "v8dbg_parent_BreakPointInfo__Struct", 0x0 }, + { "v8dbg_parent_ByteArray__HeapObject", 0x0 }, + { "v8dbg_parent_CallHandlerInfo__Struct", 0x0 }, + { "v8dbg_parent_Code__HeapObject", 0x0 }, + { "v8dbg_parent_CodeCache__Struct", 0x0 }, + { "v8dbg_parent_ConsString__String", 0x0 }, + { "v8dbg_parent_DebugInfo__Struct", 0x0 }, + { "v8dbg_parent_DeoptimizationInputData__FixedArray", 0x0 }, + { "v8dbg_parent_DeoptimizationOutputData__FixedArray", 0x0 }, + { "v8dbg_parent_DescriptorArray__FixedArray", 0x0 }, + { "v8dbg_parent_ExternalArray__HeapObject", 0x0 }, + { "v8dbg_parent_ExternalAsciiString__ExternalString", 0x0 }, + { "v8dbg_parent_ExternalByteArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalFloatArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalIntArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalShortArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalString__String", 0x0 }, + { "v8dbg_parent_ExternalTwoByteString__ExternalString", 0x0 }, + { "v8dbg_parent_ExternalUnsignedByteArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalUnsignedIntArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalUnsignedShortArray__ExternalArray", 0x0 }, + { "v8dbg_parent_Failure__MaybeObject", 0x0 }, + { "v8dbg_parent_FixedArray__HeapObject", 0x0 }, + { "v8dbg_parent_FunctionTemplateInfo__TemplateInfo", 0x0 }, + { "v8dbg_parent_GlobalObject__JSObject", 0x0 }, + { "v8dbg_parent_HeapNumber__HeapObject", 0x0 }, + { "v8dbg_parent_HeapObject__Object", 0x0 }, + { "v8dbg_parent_InterceptorInfo__Struct", 0x0 }, + { "v8dbg_parent_JSArray__JSObject", 0x0 }, + { "v8dbg_parent_JSBuiltinsObject__GlobalObject", 0x0 }, + { "v8dbg_parent_JSFunction__JSObject", 0x0 }, + { "v8dbg_parent_JSFunctionResultCache__FixedArray", 0x0 }, + { "v8dbg_parent_JSGlobalObject__GlobalObject", 0x0 }, + { "v8dbg_parent_JSGlobalPropertyCell__HeapObject", 0x0 }, + { "v8dbg_parent_JSGlobalProxy__JSObject", 0x0 }, + { "v8dbg_parent_JSMessageObject__JSObject", 0x0 }, + { "v8dbg_parent_JSObject__HeapObject", 0x0 }, + { "v8dbg_parent_JSRegExp__JSObject", 0x0 }, + { "v8dbg_parent_JSRegExpResult__JSArray", 0x0 }, + { "v8dbg_parent_JSValue__JSObject", 0x0 }, + { "v8dbg_parent_Map__HeapObject", 0x0 }, + { "v8dbg_parent_NormalizedMapCache__FixedArray", 0x0 }, + { "v8dbg_parent_Object__MaybeObject", 0x0 }, + { "v8dbg_parent_ObjectTemplateInfo__TemplateInfo", 0x0 }, + { "v8dbg_parent_Oddball__HeapObject", 0x0 }, + { "v8dbg_parent_Script__Struct", 0x0 }, + { "v8dbg_parent_SeqAsciiString__SeqString", 0x0 }, + { "v8dbg_parent_SeqString__String", 0x0 }, + { "v8dbg_parent_SeqTwoByteString__SeqString", 0x0 }, + { "v8dbg_parent_SharedFunctionInfo__HeapObject", 0x0 }, + { "v8dbg_parent_SignatureInfo__Struct", 0x0 }, + { "v8dbg_parent_Smi__Object", 0x0 }, + { "v8dbg_parent_String__HeapObject", 0x0 }, + { "v8dbg_parent_Struct__HeapObject", 0x0 }, + { "v8dbg_parent_TemplateInfo__Struct", 0x0 }, + { "v8dbg_parent_TypeSwitchInfo__Struct", 0x0 }, + + { "v8dbg_frametype_ArgumentsAdaptorFrame", 0x8 }, + { "v8dbg_frametype_ConstructFrame", 0x7 }, + { "v8dbg_frametype_EntryConstructFrame", 0x2 }, + { "v8dbg_frametype_EntryFrame", 0x1 }, + { "v8dbg_frametype_ExitFrame", 0x3 }, + { "v8dbg_frametype_InternalFrame", 0x6 }, + { "v8dbg_frametype_JavaScriptFrame", 0x4 }, + { "v8dbg_frametype_OptimizedFrame", 0x5 }, + + { "v8dbg_off_fp_context", -0x4 }, + { "v8dbg_off_fp_function", -0x8 }, + { "v8dbg_off_fp_marker", -0x8 }, + { "v8dbg_off_fp_args", 0x8 }, + + { "v8dbg_prop_idx_content", 0x0 }, + { "v8dbg_prop_idx_first", 0x2 }, + { "v8dbg_prop_type_field", 0x1 }, + { "v8dbg_prop_type_first_phantom", 0x6 }, + { "v8dbg_prop_type_mask", 0xf }, + + { "v8dbg_AsciiStringTag", 0x4 }, + { "v8dbg_ConsStringTag", 0x1 }, + { "v8dbg_ExternalStringTag", 0x2 }, + { "v8dbg_FailureTag", 0x3 }, + { "v8dbg_FailureTagMask", 0x3 }, + { "v8dbg_FirstNonstringType", 0x80 }, + { "v8dbg_HeapObjectTag", 0x1 }, + { "v8dbg_HeapObjectTagMask", 0x3 }, + { "v8dbg_IsNotStringMask", 0x80 }, + { "v8dbg_NotStringTag", 0x80 }, + { "v8dbg_SeqStringTag", 0x0 }, + { "v8dbg_SmiTag", 0x0 }, + { "v8dbg_SmiTagMask", 0x1 }, + { "v8dbg_SmiValueShift", 0x1 }, + { "v8dbg_StringEncodingMask", 0x4 }, + { "v8dbg_StringRepresentationMask", 0x3 }, + { "v8dbg_StringTag", 0x0 }, + { "v8dbg_TwoByteStringTag", 0x0 }, + { "v8dbg_PointerSizeLog2", 0x2 }, + + { NULL } +}; + +/* + * Canned configuration for the V8 bundled with Node.js v0.6.5. + */ +static v8_cfg_symbol_t v8_symbols_node_06[] = { + { "v8dbg_type_AccessCheckInfo__ACCESS_CHECK_INFO_TYPE", 0x93 }, + { "v8dbg_type_AccessorInfo__ACCESSOR_INFO_TYPE", 0x92 }, + { "v8dbg_type_BreakPointInfo__BREAK_POINT_INFO_TYPE", 0x9e }, + { "v8dbg_type_ByteArray__BYTE_ARRAY_TYPE", 0x86 }, + { "v8dbg_type_CallHandlerInfo__CALL_HANDLER_INFO_TYPE", 0x95 }, + { "v8dbg_type_Code__CODE_TYPE", 0x81 }, + { "v8dbg_type_CodeCache__CODE_CACHE_TYPE", 0x9b }, + { "v8dbg_type_ConsString__CONS_ASCII_STRING_TYPE", 0x5 }, + { "v8dbg_type_ConsString__CONS_ASCII_SYMBOL_TYPE", 0x45 }, + { "v8dbg_type_ConsString__CONS_STRING_TYPE", 0x1 }, + { "v8dbg_type_ConsString__CONS_SYMBOL_TYPE", 0x41 }, + { "v8dbg_type_DebugInfo__DEBUG_INFO_TYPE", 0x9d }, + { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_STRING_TYPE", 0x6 }, + { "v8dbg_type_ExternalAsciiString__EXTERNAL_ASCII_SYMBOL_TYPE", 0x46 }, + { "v8dbg_type_ExternalByteArray__EXTERNAL_BYTE_ARRAY_TYPE", 0x87 }, + { "v8dbg_type_ExternalDoubleArray__EXTERNAL_DOUBLE_ARRAY_TYPE", 0x8e }, + { "v8dbg_type_ExternalFloatArray__EXTERNAL_FLOAT_ARRAY_TYPE", 0x8d }, + { "v8dbg_type_ExternalIntArray__EXTERNAL_INT_ARRAY_TYPE", 0x8b }, + { "v8dbg_type_ExternalPixelArray__EXTERNAL_PIXEL_ARRAY_TYPE", 0x8f }, + { "v8dbg_type_ExternalShortArray__EXTERNAL_SHORT_ARRAY_TYPE", 0x89 }, + { "v8dbg_type_ExternalTwoByteString__EXTERNAL_STRING_TYPE", 0x2 }, + { "v8dbg_type_ExternalTwoByteString__EXTERNAL_SYMBOL_TYPE", 0x42 }, + { "v8dbg_type_ExternalUnsignedByteArray__" + "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE", 0x88 }, + { "v8dbg_type_ExternalUnsignedIntArray__" + "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE", 0x8c }, + { "v8dbg_type_ExternalUnsignedShortArray__" + "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE", 0x8a }, + { "v8dbg_type_FixedArray__FIXED_ARRAY_TYPE", 0x9f }, + { "v8dbg_type_FixedDoubleArray__FIXED_DOUBLE_ARRAY_TYPE", 0x90 }, + { "v8dbg_type_Foreign__FOREIGN_TYPE", 0x85 }, + { "v8dbg_type_FunctionTemplateInfo__FUNCTION_TEMPLATE_INFO_TYPE", + 0x96 }, + { "v8dbg_type_HeapNumber__HEAP_NUMBER_TYPE", 0x84 }, + { "v8dbg_type_InterceptorInfo__INTERCEPTOR_INFO_TYPE", 0x94 }, + { "v8dbg_type_JSArray__JS_ARRAY_TYPE", 0xa8 }, + { "v8dbg_type_JSBuiltinsObject__JS_BUILTINS_OBJECT_TYPE", 0xa6 }, + { "v8dbg_type_JSFunction__JS_FUNCTION_TYPE", 0xac }, + { "v8dbg_type_JSFunctionProxy__JS_FUNCTION_PROXY_TYPE", 0xad }, + { "v8dbg_type_JSGlobalObject__JS_GLOBAL_OBJECT_TYPE", 0xa5 }, + { "v8dbg_type_JSGlobalPropertyCell__JS_GLOBAL_PROPERTY_CELL_TYPE", + 0x83 }, + { "v8dbg_type_JSMessageObject__JS_MESSAGE_OBJECT_TYPE", 0xa1 }, + { "v8dbg_type_JSObject__JS_OBJECT_TYPE", 0xa3 }, + { "v8dbg_type_JSProxy__JS_PROXY_TYPE", 0xa9 }, + { "v8dbg_type_JSRegExp__JS_REGEXP_TYPE", 0xab }, + { "v8dbg_type_JSValue__JS_VALUE_TYPE", 0xa2 }, + { "v8dbg_type_JSWeakMap__JS_WEAK_MAP_TYPE", 0xaa }, + { "v8dbg_type_Map__MAP_TYPE", 0x80 }, + { "v8dbg_type_ObjectTemplateInfo__OBJECT_TEMPLATE_INFO_TYPE", 0x97 }, + { "v8dbg_type_Oddball__ODDBALL_TYPE", 0x82 }, + { "v8dbg_type_PolymorphicCodeCache__POLYMORPHIC_CODE_CACHE_TYPE", + 0x9c }, + { "v8dbg_type_Script__SCRIPT_TYPE", 0x9a }, + { "v8dbg_type_SeqAsciiString__ASCII_STRING_TYPE", 0x4 }, + { "v8dbg_type_SeqAsciiString__ASCII_SYMBOL_TYPE", 0x44 }, + { "v8dbg_type_SeqTwoByteString__STRING_TYPE", 0x0 }, + { "v8dbg_type_SeqTwoByteString__SYMBOL_TYPE", 0x40 }, + { "v8dbg_type_SharedFunctionInfo__SHARED_FUNCTION_INFO_TYPE", 0xa0 }, + { "v8dbg_type_SignatureInfo__SIGNATURE_INFO_TYPE", 0x98 }, + { "v8dbg_type_SlicedString__SLICED_ASCII_STRING_TYPE", 0x7 }, + { "v8dbg_type_SlicedString__SLICED_STRING_TYPE", 0x3 }, + { "v8dbg_type_TypeSwitchInfo__TYPE_SWITCH_INFO_TYPE", 0x99 }, + + { "v8dbg_class_AccessCheckInfo__data__Object", 0xc }, + { "v8dbg_class_AccessCheckInfo__indexed_callback__Object", 0x8 }, + { "v8dbg_class_AccessCheckInfo__named_callback__Object", 0x4 }, + { "v8dbg_class_AccessorInfo__data__Object", 0xc }, + { "v8dbg_class_AccessorInfo__flag__Smi", 0x14 }, + { "v8dbg_class_AccessorInfo__getter__Object", 0x4 }, + { "v8dbg_class_AccessorInfo__name__Object", 0x10 }, + { "v8dbg_class_AccessorInfo__setter__Object", 0x8 }, + { "v8dbg_class_BreakPointInfo__break_point_objects__Object", 0x10 }, + { "v8dbg_class_BreakPointInfo__code_position__Smi", 0x4 }, + { "v8dbg_class_BreakPointInfo__source_position__Smi", 0x8 }, + { "v8dbg_class_BreakPointInfo__statement_position__Smi", 0xc }, + { "v8dbg_class_CallHandlerInfo__callback__Object", 0x4 }, + { "v8dbg_class_CallHandlerInfo__data__Object", 0x8 }, + { "v8dbg_class_Code__deoptimization_data__FixedArray", 0xc }, + { "v8dbg_class_Code__instruction_size__int", 0x4 }, + { "v8dbg_class_Code__instruction_start__int", 0x20 }, + { "v8dbg_class_Code__next_code_flushing_candidate__Object", 0x10 }, + { "v8dbg_class_Code__relocation_info__ByteArray", 0x8 }, + { "v8dbg_class_CodeCache__default_cache__FixedArray", 0x4 }, + { "v8dbg_class_CodeCache__normal_type_cache__Object", 0x8 }, + { "v8dbg_class_ConsString__first__String", 0xc }, + { "v8dbg_class_ConsString__second__String", 0x10 }, + { "v8dbg_class_DebugInfo__break_points__FixedArray", 0x14 }, + { "v8dbg_class_DebugInfo__code__Code", 0xc }, + { "v8dbg_class_DebugInfo__original_code__Code", 0x8 }, + { "v8dbg_class_DebugInfo__shared__SharedFunctionInfo", 0x4 }, + { "v8dbg_class_ExternalString__resource__Object", 0xc }, + { "v8dbg_class_FixedArray__data__uintptr_t", 0x8 }, + { "v8dbg_class_FixedArrayBase__length__SMI", 0x4 }, + { "v8dbg_class_FunctionTemplateInfo__access_check_info__Object", 0x38 }, + { "v8dbg_class_FunctionTemplateInfo__call_code__Object", 0x10 }, + { "v8dbg_class_FunctionTemplateInfo__class_name__Object", 0x2c }, + { "v8dbg_class_FunctionTemplateInfo__flag__Smi", 0x3c }, + { "v8dbg_class_FunctionTemplateInfo__indexed_property_handler__Object", + 0x24 }, + { "v8dbg_class_FunctionTemplateInfo__instance_call_handler__Object", + 0x34 }, + { "v8dbg_class_FunctionTemplateInfo__instance_template__Object", 0x28 }, + { "v8dbg_class_FunctionTemplateInfo__named_property_handler__Object", + 0x20 }, + { "v8dbg_class_FunctionTemplateInfo__parent_template__Object", 0x1c }, + { "v8dbg_class_FunctionTemplateInfo__property_accessors__Object", + 0x14 }, + { "v8dbg_class_FunctionTemplateInfo__prototype_template__Object", + 0x18 }, + { "v8dbg_class_FunctionTemplateInfo__serial_number__Object", 0xc }, + { "v8dbg_class_FunctionTemplateInfo__signature__Object", 0x30 }, + { "v8dbg_class_GlobalObject__builtins__JSBuiltinsObject", 0xc }, + { "v8dbg_class_GlobalObject__global_context__Context", 0x10 }, + { "v8dbg_class_GlobalObject__global_receiver__JSObject", 0x14 }, + { "v8dbg_class_HeapNumber__value__double", 0x4 }, + { "v8dbg_class_HeapObject__map__Map", 0x0 }, + { "v8dbg_class_InterceptorInfo__data__Object", 0x18 }, + { "v8dbg_class_InterceptorInfo__deleter__Object", 0x10 }, + { "v8dbg_class_InterceptorInfo__enumerator__Object", 0x14 }, + { "v8dbg_class_InterceptorInfo__getter__Object", 0x4 }, + { "v8dbg_class_InterceptorInfo__query__Object", 0xc }, + { "v8dbg_class_InterceptorInfo__setter__Object", 0x8 }, + { "v8dbg_class_JSArray__length__Object", 0xc }, + { "v8dbg_class_JSFunction__literals__FixedArray", 0x1c }, + { "v8dbg_class_JSFunction__next_function_link__Object", 0x20 }, + { "v8dbg_class_JSFunction__prototype_or_initial_map__Object", 0x10 }, + { "v8dbg_class_JSFunction__shared__SharedFunctionInfo", 0x14 }, + { "v8dbg_class_JSFunctionProxy__call_trap__Object", 0x8 }, + { "v8dbg_class_JSFunctionProxy__construct_trap__Object", 0xc }, + { "v8dbg_class_JSGlobalProxy__context__Object", 0xc }, + { "v8dbg_class_JSMessageObject__arguments__JSArray", 0x10 }, + { "v8dbg_class_JSMessageObject__end_position__SMI", 0x24 }, + { "v8dbg_class_JSMessageObject__script__Object", 0x14 }, + { "v8dbg_class_JSMessageObject__stack_frames__Object", 0x1c }, + { "v8dbg_class_JSMessageObject__stack_trace__Object", 0x18 }, + { "v8dbg_class_JSMessageObject__start_position__SMI", 0x20 }, + { "v8dbg_class_JSMessageObject__type__String", 0xc }, + { "v8dbg_class_JSObject__elements__Object", 0x8 }, + { "v8dbg_class_JSObject__properties__FixedArray", 0x4 }, + { "v8dbg_class_JSProxy__handler__Object", 0x4 }, + { "v8dbg_class_JSRegExp__data__Object", 0xc }, + { "v8dbg_class_JSValue__value__Object", 0xc }, + { "v8dbg_class_JSWeakMap__next__Object", 0x10 }, + { "v8dbg_class_JSWeakMap__table__ObjectHashTable", 0xc }, + { "v8dbg_class_Map__code_cache__Object", 0x18 }, + { "v8dbg_class_Map__constructor__Object", 0x10 }, + { "v8dbg_class_Map__inobject_properties__int", 0x5 }, + { "v8dbg_class_Map__instance_attributes__int", 0x8 }, + { "v8dbg_class_Map__instance_descriptors__FixedArray", 0x14 }, + { "v8dbg_class_Map__instance_size__int", 0x4 }, + { "v8dbg_class_Map__prototype_transitions__FixedArray", 0x1c }, + { "v8dbg_class_ObjectTemplateInfo__constructor__Object", 0xc }, + { "v8dbg_class_ObjectTemplateInfo__internal_field_count__Object", + 0x10 }, + { "v8dbg_class_Oddball__to_number__Object", 0x8 }, + { "v8dbg_class_Oddball__to_string__String", 0x4 }, + { "v8dbg_class_PolymorphicCodeCache__cache__Object", 0x4 }, + { "v8dbg_class_Script__column_offset__Smi", 0x10 }, + { "v8dbg_class_Script__compilation_type__Smi", 0x24 }, + { "v8dbg_class_Script__context_data__Object", 0x18 }, + { "v8dbg_class_Script__data__Object", 0x14 }, + { "v8dbg_class_Script__eval_from_instructions_offset__Smi", 0x34 }, + { "v8dbg_class_Script__eval_from_shared__Object", 0x30 }, + { "v8dbg_class_Script__id__Object", 0x2c }, + { "v8dbg_class_Script__line_ends__Object", 0x28 }, + { "v8dbg_class_Script__line_offset__Smi", 0xc }, + { "v8dbg_class_Script__name__Object", 0x8 }, + { "v8dbg_class_Script__source__Object", 0x4 }, + { "v8dbg_class_Script__type__Smi", 0x20 }, + { "v8dbg_class_Script__wrapper__Foreign", 0x1c }, + { "v8dbg_class_SeqAsciiString__chars__char", 0xc }, + { "v8dbg_class_SharedFunctionInfo__code__Code", 0x8 }, + { "v8dbg_class_SharedFunctionInfo__compiler_hints__SMI", 0x50 }, + { "v8dbg_class_SharedFunctionInfo__construct_stub__Code", 0x10 }, + { "v8dbg_class_SharedFunctionInfo__debug_info__Object", 0x20 }, + { "v8dbg_class_SharedFunctionInfo__end_position__SMI", 0x48 }, + { "v8dbg_class_SharedFunctionInfo__expected_nof_properties__SMI", + 0x3c }, + { "v8dbg_class_SharedFunctionInfo__formal_parameter_count__SMI", 0x38 }, + { "v8dbg_class_SharedFunctionInfo__function_data__Object", 0x18 }, + { "v8dbg_class_SharedFunctionInfo__function_token_position__SMI", + 0x4c }, + { "v8dbg_class_SharedFunctionInfo__inferred_name__String", 0x24 }, + { "v8dbg_class_SharedFunctionInfo__initial_map__Object", 0x28 }, + { "v8dbg_class_SharedFunctionInfo__instance_class_name__Object", 0x14 }, + { "v8dbg_class_SharedFunctionInfo__length__SMI", 0x34 }, + { "v8dbg_class_SharedFunctionInfo__name__Object", 0x4 }, + { "v8dbg_class_SharedFunctionInfo__num_literals__SMI", 0x40 }, + { "v8dbg_class_SharedFunctionInfo__opt_count__SMI", 0x58 }, + { "v8dbg_class_SharedFunctionInfo__script__Object", 0x1c }, + { "v8dbg_class_SharedFunctionInfo__" + "start_position_and_type__SMI", 0x44 }, + { "v8dbg_class_SharedFunctionInfo__" + "this_property_assignments__Object", 0x2c }, + { "v8dbg_class_SharedFunctionInfo__" + "this_property_assignments_count__SMI", 0x54 }, + { "v8dbg_class_SignatureInfo__args__Object", 0x8 }, + { "v8dbg_class_SignatureInfo__receiver__Object", 0x4 }, + { "v8dbg_class_SlicedString__offset__SMI", 0x10 }, + { "v8dbg_class_String__length__SMI", 0x4 }, + { "v8dbg_class_TemplateInfo__property_list__Object", 0x8 }, + { "v8dbg_class_TemplateInfo__tag__Object", 0x4 }, + { "v8dbg_class_TypeSwitchInfo__types__Object", 0x4 }, + + { "v8dbg_parent_AccessCheckInfo__Struct", 0x0 }, + { "v8dbg_parent_AccessorInfo__Struct", 0x0 }, + { "v8dbg_parent_BreakPointInfo__Struct", 0x0 }, + { "v8dbg_parent_ByteArray__FixedArrayBase", 0x0 }, + { "v8dbg_parent_CallHandlerInfo__Struct", 0x0 }, + { "v8dbg_parent_Code__HeapObject", 0x0 }, + { "v8dbg_parent_CodeCache__Struct", 0x0 }, + { "v8dbg_parent_ConsString__String", 0x0 }, + { "v8dbg_parent_DebugInfo__Struct", 0x0 }, + { "v8dbg_parent_DeoptimizationInputData__FixedArray", 0x0 }, + { "v8dbg_parent_DeoptimizationOutputData__FixedArray", 0x0 }, + { "v8dbg_parent_DescriptorArray__FixedArray", 0x0 }, + { "v8dbg_parent_ExternalArray__FixedArrayBase", 0x0 }, + { "v8dbg_parent_ExternalAsciiString__ExternalString", 0x0 }, + { "v8dbg_parent_ExternalByteArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalDoubleArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalFloatArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalIntArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalPixelArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalShortArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalString__String", 0x0 }, + { "v8dbg_parent_ExternalTwoByteString__ExternalString", 0x0 }, + { "v8dbg_parent_ExternalUnsignedByteArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalUnsignedIntArray__ExternalArray", 0x0 }, + { "v8dbg_parent_ExternalUnsignedShortArray__ExternalArray", 0x0 }, + { "v8dbg_parent_Failure__MaybeObject", 0x0 }, + { "v8dbg_parent_FixedArray__FixedArrayBase", 0x0 }, + { "v8dbg_parent_FixedArrayBase__HeapObject", 0x0 }, + { "v8dbg_parent_FixedDoubleArray__FixedArrayBase", 0x0 }, + { "v8dbg_parent_Foreign__HeapObject", 0x0 }, + { "v8dbg_parent_FunctionTemplateInfo__TemplateInfo", 0x0 }, + { "v8dbg_parent_GlobalObject__JSObject", 0x0 }, + { "v8dbg_parent_HashTable__FixedArray", 0x0 }, + { "v8dbg_parent_HeapNumber__HeapObject", 0x0 }, + { "v8dbg_parent_HeapObject__Object", 0x0 }, + { "v8dbg_parent_InterceptorInfo__Struct", 0x0 }, + { "v8dbg_parent_JSArray__JSObject", 0x0 }, + { "v8dbg_parent_JSBuiltinsObject__GlobalObject", 0x0 }, + { "v8dbg_parent_JSFunction__JSObject", 0x0 }, + { "v8dbg_parent_JSFunctionProxy__JSProxy", 0x0 }, + { "v8dbg_parent_JSFunctionResultCache__FixedArray", 0x0 }, + { "v8dbg_parent_JSGlobalObject__GlobalObject", 0x0 }, + { "v8dbg_parent_JSGlobalPropertyCell__HeapObject", 0x0 }, + { "v8dbg_parent_JSMessageObject__JSObject", 0x0 }, + { "v8dbg_parent_JSObject__JSReceiver", 0x0 }, + { "v8dbg_parent_JSProxy__JSReceiver", 0x0 }, + { "v8dbg_parent_JSReceiver__HeapObject", 0x0 }, + { "v8dbg_parent_JSRegExp__JSObject", 0x0 }, + { "v8dbg_parent_JSRegExpResult__JSArray", 0x0 }, + { "v8dbg_parent_JSValue__JSObject", 0x0 }, + { "v8dbg_parent_JSWeakMap__JSObject", 0x0 }, + { "v8dbg_parent_Map__HeapObject", 0x0 }, + { "v8dbg_parent_NormalizedMapCache__FixedArray", 0x0 }, + { "v8dbg_parent_ObjectTemplateInfo__TemplateInfo", 0x0 }, + { "v8dbg_parent_Oddball__HeapObject", 0x0 }, + { "v8dbg_parent_PolymorphicCodeCache__Struct", 0x0 }, + { "v8dbg_parent_Script__Struct", 0x0 }, + { "v8dbg_parent_SeqAsciiString__SeqString", 0x0 }, + { "v8dbg_parent_SeqString__String", 0x0 }, + { "v8dbg_parent_SeqTwoByteString__SeqString", 0x0 }, + { "v8dbg_parent_SharedFunctionInfo__HeapObject", 0x0 }, + { "v8dbg_parent_SignatureInfo__Struct", 0x0 }, + { "v8dbg_parent_SlicedString__String", 0x0 }, + { "v8dbg_parent_Smi__Object", 0x0 }, + { "v8dbg_parent_String__HeapObject", 0x0 }, + { "v8dbg_parent_Struct__HeapObject", 0x0 }, + { "v8dbg_parent_TemplateInfo__Struct", 0x0 }, + { "v8dbg_parent_TypeSwitchInfo__Struct", 0x0 }, + + { "v8dbg_frametype_ArgumentsAdaptorFrame", 0x8 }, + { "v8dbg_frametype_ConstructFrame", 0x7 }, + { "v8dbg_frametype_EntryConstructFrame", 0x2 }, + { "v8dbg_frametype_EntryFrame", 0x1 }, + { "v8dbg_frametype_ExitFrame", 0x3 }, + { "v8dbg_frametype_InternalFrame", 0x6 }, + { "v8dbg_frametype_JavaScriptFrame", 0x4 }, + { "v8dbg_frametype_OptimizedFrame", 0x5 }, + + { "v8dbg_off_fp_args", 0x8 }, + { "v8dbg_off_fp_context", -0x4 }, + { "v8dbg_off_fp_function", -0x8 }, + { "v8dbg_off_fp_marker", -0x8 }, + + { "v8dbg_prop_idx_content", 0x1 }, + { "v8dbg_prop_idx_first", 0x3 }, + { "v8dbg_prop_type_field", 0x1 }, + { "v8dbg_prop_type_first_phantom", 0x6 }, + { "v8dbg_prop_type_mask", 0xf }, + + { "v8dbg_AsciiStringTag", 0x4 }, + { "v8dbg_PointerSizeLog2", 0x2 }, + { "v8dbg_SeqStringTag", 0x0 }, + { "v8dbg_SmiTag", 0x0 }, + { "v8dbg_SmiTagMask", 0x1 }, + { "v8dbg_SmiValueShift", 0x1 }, + { "v8dbg_StringEncodingMask", 0x4 }, + { "v8dbg_StringRepresentationMask", 0x3 }, + { "v8dbg_StringTag", 0x0 }, + { "v8dbg_TwoByteStringTag", 0x0 }, + { "v8dbg_ConsStringTag", 0x1 }, + { "v8dbg_ExternalStringTag", 0x2 }, + { "v8dbg_FailureTag", 0x3 }, + { "v8dbg_FailureTagMask", 0x3 }, + { "v8dbg_FirstNonstringType", 0x80 }, + { "v8dbg_HeapObjectTag", 0x1 }, + { "v8dbg_HeapObjectTagMask", 0x3 }, + { "v8dbg_IsNotStringMask", 0x80 }, + { "v8dbg_NotStringTag", 0x80 }, + + { NULL }, +}; + +v8_cfg_t v8_cfg_04 = { "node-0.4", "node v0.4", v8_symbols_node_04, + v8cfg_canned_iter, v8cfg_canned_readsym }; + +v8_cfg_t v8_cfg_06 = { "node-0.6", "node v0.6", v8_symbols_node_06, + v8cfg_canned_iter, v8cfg_canned_readsym }; + +v8_cfg_t *v8_cfgs[] = { + &v8_cfg_04, + &v8_cfg_06, + NULL +}; + +v8_cfg_t v8_cfg_target = { NULL, NULL, NULL, v8cfg_target_iter, + v8cfg_target_readsym }; diff --git a/usr/src/cmd/mdb/common/modules/v8/v8cfg.h b/usr/src/cmd/mdb/common/modules/v8/v8cfg.h new file mode 100644 index 0000000000..9e722b0e2b --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/v8/v8cfg.h @@ -0,0 +1,55 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ + +/* + * v8cfg.h: canned configurations for previous V8 versions + */ + +#ifndef V8CFG_H +#define V8CFG_H + +#include <sys/types.h> +#include <sys/mdb_modapi.h> + +typedef struct { + const char *v8cs_name; /* symbol name */ + intptr_t v8cs_value; /* symbol value */ +} v8_cfg_symbol_t; + +typedef struct v8_cfg { + const char *v8cfg_name; /* canned config name */ + const char *v8cfg_label; /* description */ + v8_cfg_symbol_t *v8cfg_symbols; /* actual symbol values */ + + int (*v8cfg_iter)(struct v8_cfg *, int (*)(mdb_symbol_t *, void *), + void *); + int (*v8cfg_readsym)(struct v8_cfg *, const char *, intptr_t *); +} v8_cfg_t; + +extern v8_cfg_t v8_cfg_04; +extern v8_cfg_t v8_cfg_06; +extern v8_cfg_t v8_cfg_target; +extern v8_cfg_t *v8_cfgs[]; + +#endif /* V8CFG_H */ diff --git a/usr/src/cmd/mdb/common/modules/v8/v8dbg.h b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h new file mode 100644 index 0000000000..b17f241fac --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/v8/v8dbg.h @@ -0,0 +1,91 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + +/* + * v8dbg.h: macros for use by V8 heap inspection tools. The consumer must + * define values for various tags and shifts. The MDB module gets these + * constants from information encoded in the binary itself. + */ + +#ifndef _V8DBG_H +#define _V8DBG_H + +/* + * Recall that while V8 heap objects are always 4-byte aligned, heap object + * pointers always have the last bit set. So when looking for a field nominally + * at offset X, one must be sure to clear the tag bit first. + */ +#define V8_OFF_HEAP(x) ((x) - V8_HeapObjectTag) + +/* + * Determine whether a given pointer refers to a SMI, Failure, or HeapObject. + */ +#define V8_IS_SMI(ptr) (((ptr) & V8_SmiTagMask) == V8_SmiTag) +#define V8_IS_FAILURE(ptr) (V8_FailureTagMask != -1 && \ + V8_FailureTagMask != -1 && \ + ((ptr) & V8_FailureTagMask) == V8_FailureTag) + +#define V8_IS_HEAPOBJECT(ptr) \ + (((ptr) & V8_HeapObjectTagMask) == V8_HeapObjectTag) + +/* + * Extract the value of a SMI "pointer". Recall that small integers are stored + * using the upper 31 bits. + */ +#define V8_SMI_VALUE(smi) ((smi) >> (V8_SmiValueShift + V8_SmiShiftSize)) +#define V8_VALUE_SMI(value) \ + ((value) << (V8_SmiValueShift + V8_SmiShiftSize)) + +/* + * Determine the encoding and representation of a V8 string. + */ +#define V8_TYPE_STRING(type) (((type) & V8_IsNotStringMask) == V8_StringTag) + +#define V8_STRENC_ASCII(type) \ + (((type) & V8_StringEncodingMask) == V8_AsciiStringTag) + +#define V8_STRREP_SEQ(type) \ + (((type) & V8_StringRepresentationMask) == V8_SeqStringTag) +#define V8_STRREP_CONS(type) \ + (((type) & V8_StringRepresentationMask) == V8_ConsStringTag) +#define V8_STRREP_SLICED(type) \ + (((type) & V8_StringRepresentationMask) == V8_SlicedStringTag) +#define V8_STRREP_EXT(type) \ + (((type) & V8_StringRepresentationMask) == V8_ExternalStringTag) + +/* + * Several of the following constants and transformations are hardcoded in V8 as + * well, so there's no way to extract them programmatically from the binary. + */ +#define V8_DESC_KEYIDX(x) ((x) + V8_PROP_IDX_FIRST) +#define V8_DESC_VALIDX(x) ((x) << 1) +#define V8_DESC_DETIDX(x) (((x) << 1) + 1) + +#define V8_DESC_ISFIELD(x) \ + ((V8_SMI_VALUE(x) & V8_PROP_TYPE_MASK) == V8_PROP_TYPE_FIELD) + +#define V8_PROP_FIELDINDEX(value) \ + ((V8_SMI_VALUE(value) & V8_FIELDINDEX_MASK) >> V8_FIELDINDEX_SHIFT) + +#endif /* _V8DBG_H */ diff --git a/usr/src/cmd/mdb/i86pc/modules/unix/amd64/Makefile b/usr/src/cmd/mdb/i86pc/modules/unix/amd64/Makefile index 8d36fb01e5..26afa1c288 100644 --- a/usr/src/cmd/mdb/i86pc/modules/unix/amd64/Makefile +++ b/usr/src/cmd/mdb/i86pc/modules/unix/amd64/Makefile @@ -27,6 +27,7 @@ MODULE = unix.so MDBTGT = kvm MODSRCS = unix.c i86mmu.c +MODASMSRCS = unix_sup.s include ../../../../../Makefile.cmd include ../../../../../Makefile.cmd.64 diff --git a/usr/src/cmd/mdb/i86pc/modules/unix/ia32/Makefile b/usr/src/cmd/mdb/i86pc/modules/unix/ia32/Makefile index ad756f82e9..2c76a010bd 100644 --- a/usr/src/cmd/mdb/i86pc/modules/unix/ia32/Makefile +++ b/usr/src/cmd/mdb/i86pc/modules/unix/ia32/Makefile @@ -27,6 +27,7 @@ MODULE = unix.so MDBTGT = kvm MODSRCS = unix.c i86mmu.c +MODASMSRCS = unix_sup.s include ../../../../../Makefile.cmd include ../../../../intel/Makefile.ia32 diff --git a/usr/src/cmd/mdb/i86pc/modules/unix/unix.c b/usr/src/cmd/mdb/i86pc/modules/unix/unix.c index d774cde91f..df497760d9 100644 --- a/usr/src/cmd/mdb/i86pc/modules/unix/unix.c +++ b/usr/src/cmd/mdb/i86pc/modules/unix/unix.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Joyent, Inc. */ #include <mdb/mdb_modapi.h> @@ -35,7 +36,11 @@ #include <sys/mutex.h> #include <sys/mutex_impl.h> #include "i86mmu.h" +#include "unix_sup.h" #include <sys/apix.h> +#include <sys/x86_archext.h> +#include <sys/bitmap.h> +#include <sys/controlregs.h> #define TT_HDLR_WIDTH 17 @@ -745,6 +750,184 @@ ptable_help(void) "-m Interpret the PFN as an MFN (machine frame number)\n"); } +/* + * NSEC_SHIFT is replicated here (it is not defined in a header file), + * but for amusement, the reader is directed to the comment that explains + * the rationale for this particular value on x86. Spoiler: the value is + * selected to accommodate 60 MHz Pentiums! (And a confession: if the voice + * in that comment sounds too familiar, it's because your author also wrote + * that code -- some fifteen years prior to this writing in 2011...) + */ +#define NSEC_SHIFT 5 + +/*ARGSUSED*/ +static int +scalehrtime_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + uint32_t nsec_scale; + hrtime_t tsc = addr, hrt; + unsigned int *tscp = (unsigned int *)&tsc; + uintptr_t scalehrtimef; + uint64_t scale; + GElf_Sym sym; + + if (!(flags & DCMD_ADDRSPEC)) { + if (argc != 1) + return (DCMD_USAGE); + + switch (argv[0].a_type) { + case MDB_TYPE_STRING: + tsc = mdb_strtoull(argv[0].a_un.a_str); + break; + case MDB_TYPE_IMMEDIATE: + tsc = argv[0].a_un.a_val; + break; + default: + return (DCMD_USAGE); + } + } + + if (mdb_readsym(&scalehrtimef, + sizeof (scalehrtimef), "scalehrtimef") == -1) { + mdb_warn("couldn't read 'scalehrtimef'"); + return (DCMD_ERR); + } + + if (mdb_lookup_by_name("tsc_scalehrtime", &sym) == -1) { + mdb_warn("couldn't find 'tsc_scalehrtime'"); + return (DCMD_ERR); + } + + if (sym.st_value != scalehrtimef) { + mdb_warn("::scalehrtime requires that scalehrtimef " + "be set to tsc_scalehrtime\n"); + return (DCMD_ERR); + } + + if (mdb_readsym(&nsec_scale, sizeof (nsec_scale), "nsec_scale") == -1) { + mdb_warn("couldn't read 'nsec_scale'"); + return (DCMD_ERR); + } + + scale = (uint64_t)nsec_scale; + + hrt = ((uint64_t)tscp[1] * scale) << NSEC_SHIFT; + hrt += ((uint64_t)tscp[0] * scale) >> (32 - NSEC_SHIFT); + + mdb_printf("0x%llx\n", hrt); + + return (DCMD_OK); +} + +/* + * The x86 feature set is implemented as a bitmap array. That bitmap array is + * stored across a number of uchars based on the BT_SIZEOFMAP(NUM_X86_FEATURES) + * macro. We have the names for each of these features in unix's text segment + * so we do not have to duplicate them and instead just look them up. + */ +/*ARGSUSED*/ +static int +x86_featureset_cmd(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uchar_t *fset; + GElf_Sym sym; + uintptr_t nptr; + char name[128]; + int ii; + + size_t sz = sizeof (uchar_t) * BT_SIZEOFMAP(NUM_X86_FEATURES); + + if (argc != 0) + return (DCMD_USAGE); + + if (mdb_lookup_by_name("x86_feature_names", &sym) == -1) { + mdb_warn("couldn't find x86_feature_names"); + return (DCMD_ERR); + } + + fset = mdb_zalloc(sz, UM_NOSLEEP); + if (fset == NULL) { + mdb_warn("failed to allocate memory for x86_featureset"); + return (DCMD_ERR); + } + + if (mdb_readvar(fset, "x86_featureset") != sz) { + mdb_warn("failed to read x86_featureset"); + mdb_free(fset, sz); + return (DCMD_ERR); + } + + for (ii = 0; ii < NUM_X86_FEATURES; ii++) { + if (!BT_TEST((ulong_t *)fset, ii)) + continue; + + if (mdb_vread(&nptr, sizeof (char *), sym.st_value + + sizeof (void *) * ii) != sizeof (char *)) { + mdb_warn("failed to read feature array %d", ii); + mdb_free(fset, sz); + return (DCMD_ERR); + } + + if (mdb_readstr(name, sizeof (name), nptr) == -1) { + mdb_warn("failed to read feature %d", ii); + mdb_free(fset, sz); + return (DCMD_ERR); + } + mdb_printf("%s\n", name); + } + + mdb_free(fset, sz); + return (DCMD_OK); +} + +#ifdef _KMDB +static int +crregs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + ulong_t cr0, cr4; + static const mdb_bitmask_t cr0_flag_bits[] = { + { "PE", CR0_PE, CR0_PE }, + { "MP", CR0_MP, CR0_MP }, + { "EM", CR0_EM, CR0_EM }, + { "TS", CR0_TS, CR0_TS }, + { "ET", CR0_ET, CR0_ET }, + { "NE", CR0_NE, CR0_NE }, + { "WP", CR0_WP, CR0_WP }, + { "AM", CR0_AM, CR0_AM }, + { "NW", CR0_NW, CR0_NW }, + { "CD", CR0_CD, CR0_CD }, + { "PG", CR0_PG, CR0_PG }, + { NULL, 0, 0 } + }; + + static const mdb_bitmask_t cr4_flag_bits[] = { + { "VME", CR4_VME, CR4_VME }, + { "PVI", CR4_PVI, CR4_PVI }, + { "TSD", CR4_TSD, CR4_TSD }, + { "DE", CR4_DE, CR4_DE }, + { "PSE", CR4_PSE, CR4_PSE }, + { "PAE", CR4_PAE, CR4_PAE }, + { "MCE", CR4_MCE, CR4_MCE }, + { "PGE", CR4_PGE, CR4_PGE }, + { "PCE", CR4_PCE, CR4_PCE }, + { "OSFXSR", CR4_OSFXSR, CR4_OSFXSR }, + { "OSXMMEXCPT", CR4_OSXMMEXCPT, CR4_OSXMMEXCPT }, + { "VMXE", CR4_VMXE, CR4_VMXE }, + { "SMXE", CR4_SMXE, CR4_SMXE }, + { "OSXSAVE", CR4_OSXSAVE, CR4_OSXSAVE }, + { "SMEP", CR4_SMEP, CR4_SMEP }, + { NULL, 0, 0 } + }; + + cr0 = kmdb_unix_getcr0(); + cr4 = kmdb_unix_getcr4(); + mdb_printf("%%cr0 = 0x%08x <%b>\n", cr0, cr0, cr0_flag_bits); + mdb_printf("%%cr4 = 0x%08x <%b>\n", cr4, cr4, cr4_flag_bits); + return (DCMD_OK); +} +#endif + static const mdb_dcmd_t dcmds[] = { { "gate_desc", ":", "dump a gate descriptor", gate_desc }, { "idt", ":[-v]", "dump an IDT", idt }, @@ -765,6 +948,13 @@ static const mdb_dcmd_t dcmds[] = { { "mfntopfn", ":", "convert hypervisor machine page to physical page", mfntopfn_dcmd }, { "memseg_list", ":", "show memseg list", memseg_list }, + { "scalehrtime", ":", + "scale an unscaled high-res time", scalehrtime_cmd }, + { "x86_featureset", NULL, "dump the x86_featureset vector", + x86_featureset_cmd }, +#ifdef _KMDB + { "crregs", NULL, "dump control registers", crregs_dcmd }, +#endif { NULL } }; diff --git a/usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.h b/usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.h new file mode 100644 index 0000000000..b272baaf59 --- /dev/null +++ b/usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.h @@ -0,0 +1,36 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#ifndef _UNIX_SUP_H +#define _UNIX_SUP_H + +/* + * Support routines for unix. + */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern ulong_t kmdb_unix_getcr0(void); +extern ulong_t kmdb_unix_getcr4(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _UNIX_SUP_H */ diff --git a/usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.s b/usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.s new file mode 100644 index 0000000000..5f97c78f71 --- /dev/null +++ b/usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.s @@ -0,0 +1,62 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + + .file "unix_sup.s" + +/* + * Support routines for the unix kmdb module + */ + +#include <sys/asm_linkage.h> + +#if defined(__lint) + +#include <sys/types.h> + +ulong_t +kmdb_unix_getcr0(void) +{ return (0); } + +ulong_t +kmdb_unix_getcr4(void) +{ return (0); } + +#else /* __lint */ + +#if defined(__amd64) + ENTRY(kmdb_unix_getcr0) + movq %cr0, %rax + ret + SET_SIZE(kmdb_unix_getcr0) + + ENTRY(kmdb_unix_getcr4) + movq %cr4, %rax + ret + SET_SIZE(kmdb_unix_getcr4) + +#elif defined (__i386) + ENTRY(kmdb_unix_getcr0) + movl %cr0, %eax + ret + SET_SIZE(kmdb_unix_getcr0) + + ENTRY(kmdb_unix_getcr4) + movl %cr4, %eax + ret + SET_SIZE(kmdb_unix_getcr4) + +#endif /* __i386 */ + +#endif /* __lint */ diff --git a/usr/src/cmd/mdb/i86xpv/modules/unix/amd64/Makefile b/usr/src/cmd/mdb/i86xpv/modules/unix/amd64/Makefile index dd8ea3d585..95922ff772 100644 --- a/usr/src/cmd/mdb/i86xpv/modules/unix/amd64/Makefile +++ b/usr/src/cmd/mdb/i86xpv/modules/unix/amd64/Makefile @@ -27,6 +27,7 @@ MODULE = unix.so MDBTGT = kvm MODSRCS = unix.c i86mmu.c +MODASMSRCS = unix_sup.s include ../../../../../Makefile.cmd include ../../../../../Makefile.cmd.64 diff --git a/usr/src/cmd/mdb/i86xpv/modules/unix/ia32/Makefile b/usr/src/cmd/mdb/i86xpv/modules/unix/ia32/Makefile index fd0ad9ee04..975ae705dc 100644 --- a/usr/src/cmd/mdb/i86xpv/modules/unix/ia32/Makefile +++ b/usr/src/cmd/mdb/i86xpv/modules/unix/ia32/Makefile @@ -27,6 +27,7 @@ MODULE = unix.so MDBTGT = kvm MODSRCS = unix.c i86mmu.c +MODASMSRCS = unix_sup.s include ../../../../../Makefile.cmd include ../../../../intel/Makefile.ia32 diff --git a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile index 704ff65873..ae22217a1b 100644 --- a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile +++ b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/intel/amd64/v8/Makefile b/usr/src/cmd/mdb/intel/amd64/v8/Makefile new file mode 100644 index 0000000000..cf74ba1d1c --- /dev/null +++ b/usr/src/cmd/mdb/intel/amd64/v8/Makefile @@ -0,0 +1,45 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +MODULE = v8.so +MDBTGT = proc + +MODSRCS_DIR = ../../../common/modules/v8 + +MODSRCS = mdb_v8.c mdb_v8_cfg.c + +include ../../../../Makefile.cmd +include ../../../../Makefile.cmd.64 +include ../../Makefile.amd64 +include ../../../Makefile.module + +dmod/$(MODULE) := LDLIBS += -lproc -lavl + +%.o: $(MODSRCS_DIR)/%.c + $(COMPILE.c) $< + $(CTFCONVERT_O) + +%.ln: $(MODSRCS_DIR)/%.c + $(LINT.c) -c $< diff --git a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile index a1ab338f40..bde1be90ac 100644 --- a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile +++ b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/intel/ia32/v8/Makefile b/usr/src/cmd/mdb/intel/ia32/v8/Makefile new file mode 100644 index 0000000000..c84532289e --- /dev/null +++ b/usr/src/cmd/mdb/intel/ia32/v8/Makefile @@ -0,0 +1,44 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +MODULE = v8.so +MDBTGT = proc + +MODSRCS_DIR = ../../../common/modules/v8 + +MODSRCS = mdb_v8.c mdb_v8_cfg.c + +include ../../../../Makefile.cmd +include ../../Makefile.ia32 +include ../../../Makefile.module + +dmod/$(MODULE) := LDLIBS += -lproc -lavl + +%.o: $(MODSRCS_DIR)/%.c + $(COMPILE.c) $< + $(CTFCONVERT_O) + +%.ln: $(MODSRCS_DIR)/%.c + $(LINT.c) -c $< diff --git a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile index 906d05d5ea..0488e6739a 100644 --- a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile +++ b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile index 09ea0473c6..87ce977423 100644 --- a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile +++ b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/test/mtest.sh b/usr/src/cmd/mdb/test/mtest.sh index f21d0faa21..f21d0faa21 100644..100755 --- a/usr/src/cmd/mdb/test/mtest.sh +++ b/usr/src/cmd/mdb/test/mtest.sh diff --git a/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out diff --git a/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out b/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out diff --git a/usr/src/cmd/nicstat/Makefile b/usr/src/cmd/nicstat/Makefile new file mode 100644 index 0000000000..935011119a --- /dev/null +++ b/usr/src/cmd/nicstat/Makefile @@ -0,0 +1,31 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +PROG = nicstat + +include ../Makefile.cmd + +all: $(PROG) + +install: all .WAIT $(ROOTPROG) + +clean: + +$(ROOTBINPROG): $(PROG) + $(INS.file) + +lint: + +include ../Makefile.targ diff --git a/usr/src/cmd/nicstat/nicstat.pl b/usr/src/cmd/nicstat/nicstat.pl new file mode 100644 index 0000000000..fae4c797df --- /dev/null +++ b/usr/src/cmd/nicstat/nicstat.pl @@ -0,0 +1,424 @@ +#!/usr/perl5/bin/perl -w +# +# nicstat - print network traffic, Kbyte/s read and written. +# Solaris 8+, Perl (Sun::Solaris::Kstat). +# +# "netstat -i" only gives a packet count, this program gives Kbytes. +# +# 04-Apr-2011, ver 1.00J +# +# USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]] +# +# -h # help +# -s # print summary output +# -z # skip zero lines +# -i int[,int...] # print these instances only +# eg, +# nicstat # print summary since boot +# nicstat 1 # print continually, every 1 second +# nicstat 1 5 # print 5 times, every 1 second +# nicstat -i hme0 # only examine hme0 +# +# This prints out the KB/s transferred for all the network cards (NICs), +# including packet counts and average sizes. The first line is the summary +# data since boot. +# +# FIELDS: +# Int Interface +# rKB/s read Kbytes/s +# wKB/s write Kbytes/s +# rPk/s read Packets/s +# wPk/s write Packets/s +# rAvs read Average size, bytes +# wAvs write Average size, bytes +# %Util %Utilisation (r or w/ifspeed) +# Sat Saturation (defer, nocanput, norecvbuf, noxmtbuf) +# +# NOTES: +# +# - Some unusual network cards may not provide all the details to Kstat, +# (or provide different symbols). Check for newer versions of this program, +# and the @Network array in the code below. +# - Utilisation is based on bytes transferred divided by speed of the interface +# (if the speed is known). It should be impossible to reach 100% as there +# are overheads due to bus negotiation and timing. +# - Loopback interfaces may only provide packet counts (if anything), and so +# bytes and %util will always be zero. Newer versions of Solaris (newer than +# Solaris 10 6/06) may provide loopback byte stats. +# - Saturation is determined by counting read and write errors caused by the +# interface running at saturation. This approach is not ideal, and the value +# reported is often lower than it should be (eg, 0.0). Reading the rKB/s and +# wKB/s fields may be more useful. +# +# SEE ALSO: +# nicstat.c # the C version, also on my website +# kstat -n hme0 [interval [count]] # or qfe0, ... +# netstat -iI hme0 [interval [count]] +# se netstat.se [interval] # SE Toolkit +# se nx.se [interval] # SE Toolkit +# +# COPYRIGHT: Copyright (c) 2013 Brendan Gregg. +# +# 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 docs/cddl1.txt or +# http://opensource.org/licenses/CDDL-1.0. +# 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 docs/cddl1.txt. +# 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 2015 Joyent, Inc. All rights reserved. +# +# Author: Brendan Gregg [Sydney, Australia] +# +# 18-Jul-2004 Brendan Gregg Created this. +# 07-Jan-2005 " " added saturation value. +# 07-Jan-2005 " " added summary style (from Peter Tribble). +# 23-Jan-2006 " " Tweaked style. +# 11-Aug-2006 " " Improved output neatness. +# 30-Sep-2006 " " Added loopback, tweaked output. +# 04-Apr-2011 brendan@joyent.com Updated for smartmachines. + +use strict; +use Getopt::Std; +use Sun::Solaris::Kstat; +my $Kstat = Sun::Solaris::Kstat->new(); + + +# +# Process command line args +# +usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; +getopts('hi:sz') or usage(); +usage() if defined $main::opt_h; +my $STYLE = defined $main::opt_s ? $main::opt_s : 0; +my $SKIPZERO = defined $main::opt_z ? $main::opt_z : 0; + +# process [interval [count]], +my ($interval, $loop_max); +if (defined $ARGV[0]) { + $interval = $ARGV[0]; + $loop_max = defined $ARGV[1] ? $ARGV[1] : 2**32; + usage() if $interval == 0; +} +else { + $interval = 1; + $loop_max = 1; +} + +# check for -i, +my %NetworkOnly; # network interfaces to print +my $NETWORKONLY = 0; # match on network interfaces +if (defined $main::opt_i) { + foreach my $net (split /,/, $main::opt_i) { + $NetworkOnly{$net} = 1; + } + $NETWORKONLY = 1; +} + +# globals, +my $loop = 0; # current loop number +my $PAGESIZE = 20; # max lines per header +my $line = $PAGESIZE; # counter for lines printed +my %NetworkNames; # Kstat network interfaces +my %NetworkData; # network interface data +my %NetworkDataOld; # network interface data +$main::opt_h = 0; +$| = 1; # autoflush + +# kstat "link" module includes: +my @Network = qw(dmfe bge be bnx ce eri eth external ge hme igb ige internal ixgbe le net ppp qfe rtls); +my %Network; +$Network{$_} = 1 foreach (@Network); +my $ZONENAME = `/usr/bin/zonename`; +chomp $ZONENAME; + +### Determine network interfaces +unless (find_nets()) { + if ($NETWORKONLY) { + print STDERR "ERROR1: $main::opt_i matched no network interfaces.\n"; + } + else { + print STDERR "ERROR1: No network interfaces found!\n"; + } + exit 1; +} + + +# +# Main +# +while (1) { + + ### Print Header + if ($line >= $PAGESIZE) { + if ($STYLE == 0) { + printf "%8s %12s %7s %7s %7s %7s %7s %7s %6s %5s\n", + "Time", "Int", "rKB/s", "wKB/s", "rPk/s", "wPk/s", "rAvs", + "wAvs", "%Util", "Sat"; + } + elsif ($STYLE == 1) { + printf "%8s %12s %14s %14s\n", "Time", "Int", "rKB/s", "wKB/s"; + } + + $line = 0; + } + + ### Get new data + my (@NetworkData) = fetch_net_data(); + + foreach my $network_data (@NetworkData) { + + ### Extract values + my ($int, $rbytes, $wbytes, $rpackets, $wpackets, $speed, $sat, $time) + = split /:/, $network_data; + + ### Retrieve old values + my ($old_rbytes, $old_wbytes, $old_rpackets, $old_wpackets, $old_sat, + $old_time); + if (defined $NetworkDataOld{$int}) { + ($old_rbytes, $old_wbytes, $old_rpackets, $old_wpackets, + $old_sat, $old_time) = split /:/, $NetworkDataOld{$int}; + } + else { + $old_rbytes = $old_wbytes = $old_rpackets = $old_wpackets + = $old_sat = $old_time = 0; + } + + # + # Calculate statistics + # + + # delta time + my $tdiff = $time - $old_time; + + # per second values + my $rbps = ($rbytes - $old_rbytes) / $tdiff; + my $wbps = ($wbytes - $old_wbytes) / $tdiff; + my $rkps = $rbps / 1024; + my $wkps = $wbps / 1024; + my $rpps = ($rpackets - $old_rpackets) / $tdiff; + my $wpps = ($wpackets - $old_wpackets) / $tdiff; + my $ravs = $rpps > 0 ? $rbps / $rpps : 0; + my $wavs = $wpps > 0 ? $wbps / $wpps : 0; + + # skip zero lines if asked + next if $SKIPZERO and ($rbps + $wbps) == 0; + + # % utilisation + my $util; + if ($speed > 0) { + # the following has a mysterious "800", it is 100 + # for the % conversion, and 8 for bytes2bits. + my $rutil = $rbps * 800 / $speed; + my $wutil = $wbps * 800 / $speed; + $util = $rutil > $wutil ? $rutil : $wutil; + $util = 100 if $util > 100; + } + else { + $util = 0; + } + + # saturation per sec + my $sats = ($sat - $old_sat) / $tdiff; + + # + # Print statistics + # + if ($rbps ne "") { + my @Time = localtime(); + + if ($STYLE == 0) { + printf "%02d:%02d:%02d %12s ", + $Time[2], $Time[1], $Time[0], $int; + print_neat($rkps); + print_neat($wkps); + print_neat($rpps); + print_neat($wpps); + print_neat($ravs); + print_neat($wavs); + printf "%6.2f %5.2f\n", $util, $sats; + } + elsif ($STYLE == 1) { + printf "%02d:%02d:%02d %12s %14.3f %14.3f\n", + $Time[2], $Time[1], $Time[0], $int, $rkps, $wkps; + } + + $line++; + + # for multiple interfaces, always print the header + $line += $PAGESIZE if @NetworkData > 1; + } + + ### Store old values + $NetworkDataOld{$int} + = "$rbytes:$wbytes:$rpackets:$wpackets:$sat:$time"; + } + + ### Check for end + last if ++$loop == $loop_max; + + ### Interval + sleep $interval; +} + + +# find_nets - walk Kstat to discover network interfaces. +# +# This walks %Kstat and populates a %NetworkNames with discovered +# network interfaces. +# +sub find_nets { + my $found = 0; + + ### Loop over all Kstat modules + foreach my $module (keys %$Kstat) { + my $Modules = $Kstat->{$module}; + + foreach my $instance (keys %$Modules) { + my $Instances = $Modules->{$instance}; + + foreach my $name (keys %$Instances) { + + ### Skip interface if asked + if ($NETWORKONLY) { + next unless $NetworkOnly{$name}; + } + + my $Names = $Instances->{$name}; + + # Check this is a network device. + # Matching on ifspeed has been more reliable than "class" + # we also match loopback and "link" interfaces. + if (defined $$Names{ifspeed} || $module eq "lo" + || $module eq "link") { + next if $name eq "mac"; + if ($module eq "link") { + my $nname = $name; + $nname =~ s/\d+$//; + next unless defined $Network{$nname} + or $ZONENAME eq $nname + or $ZONENAME eq "global"; + } + ### Save network interface + $NetworkNames{$name} = $Names; + $found++; + } + } + } + } + + return $found; +} + +# fetch - fetch Kstat data for the network interfaces. +# +# This uses the interfaces in %NetworkNames and returns useful Kstat data. +# The Kstat values used are rbytes64, obytes64, ipackets64, opackets64 +# (or the 32 bit versions if the 64 bit values are not there). +# +sub fetch_net_data { + my ($rbytes, $wbytes, $rpackets, $wpackets, $speed, $time); + my @NetworkData = (); + + $Kstat->update(); + + ### Loop over previously found network interfaces + foreach my $name (sort keys %NetworkNames) { + my $Names = $NetworkNames{$name}; + + if (defined $$Names{opackets}) { + + ### Fetch write bytes + if (defined $$Names{obytes64}) { + $rbytes = $$Names{rbytes64}; + $wbytes = $$Names{obytes64}; + } + elsif (defined $$Names{obytes}) { + $rbytes = $$Names{rbytes}; + $wbytes = $$Names{obytes}; + } else { + $rbytes = $wbytes = 0; + } + + ### Fetch read bytes + if (defined $$Names{opackets64}) { + $rpackets = $$Names{ipackets64}; + $wpackets = $$Names{opackets64}; + } + else { + $rpackets = $$Names{ipackets}; + $wpackets = $$Names{opackets}; + } + + ### Fetch interface speed + if (defined $$Names{ifspeed}) { + $speed = $$Names{ifspeed}; + } + else { + # if we can't fetch the speed, print the + # %Util as 0.0 . To do this we, + $speed = 2 ** 48; + } + + ### Determine saturation value + my $sat = 0; + if (defined $$Names{nocanput} or defined $$Names{norcvbuf}) { + $sat += defined $$Names{defer} ? $$Names{defer} : 0; + $sat += defined $$Names{nocanput} ? $$Names{nocanput} : 0; + $sat += defined $$Names{norcvbuf} ? $$Names{norcvbuf} : 0; + $sat += defined $$Names{noxmtbuf} ? $$Names{noxmtbuf} : 0; + } + + ### use the last snaptime value, + $time = $$Names{snaptime}; + + ### store data + push @NetworkData, "$name:$rbytes:$wbytes:" . + "$rpackets:$wpackets:$speed:$sat:$time"; + } + } + + return @NetworkData; +} + +# print_neat - print a float with decimal places if appropriate. +# +# This specifically keeps the width to 7 characters, if possible, plus +# a trailing space. +# +sub print_neat { + my $num = shift; + if ($num >= 100000) { + printf "%7d ", $num; + } elsif ($num >= 100) { + printf "%7.1f ", $num; + } else { + printf "%7.2f ", $num; + } +} + +# usage - print usage and exit. +# +sub usage { + print STDERR <<END; +USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]] + eg, nicstat # print summary since boot + nicstat 1 # print continually every 1 second + nicstat 1 5 # print 5 times, every 1 second + nicstat -s # summary output + nicstat -i hme0 # print hme0 only +END + exit 1; +} diff --git a/usr/src/cmd/nscd/Makefile b/usr/src/cmd/nscd/Makefile index aa478fa3b0..1e51819bb2 100644 --- a/usr/src/cmd/nscd/Makefile +++ b/usr/src/cmd/nscd/Makefile @@ -29,6 +29,7 @@ MANIFEST= name-service-cache.xml SVCMETHOD= svc-nscd include ../Makefile.cmd +include ../Makefile.ctf ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) diff --git a/usr/src/cmd/nscd/svc-nscd b/usr/src/cmd/nscd/svc-nscd index 0c6aa1bc4b..78b318bf87 100644 --- a/usr/src/cmd/nscd/svc-nscd +++ b/usr/src/cmd/nscd/svc-nscd @@ -23,8 +23,8 @@ # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012 Joyent, Inc. All rights reserved. # -#ident "%Z%%M% %I% %E% SMI" . /lib/svc/share/smf_include.sh @@ -51,7 +51,7 @@ if (smf_is_system_labeled); then $SMF_FMRI` fi if [ "$duration" != "transient" ]; then - ( while true ; do sleep 3600 ; done ) & + exit $SMF_EXIT_NODAEMON fi # The real daemon is not started in non-global zones, diff --git a/usr/src/cmd/passwd/Makefile b/usr/src/cmd/passwd/Makefile index 561357a16c..079e8b6050 100644 --- a/usr/src/cmd/passwd/Makefile +++ b/usr/src/cmd/passwd/Makefile @@ -33,6 +33,8 @@ lint := LDLIBS += -lpasswdutil LDFLAGS += $(ZIGNORE) LDLIBS += -lbsm -lpam -lnsl +CPPFLAGS += -D__EXTENSIONS__ + FILEMODE = 06555 XGETFLAGS += -a -x $(PROG).xcl diff --git a/usr/src/cmd/pgrep/pgrep.c b/usr/src/cmd/pgrep/pgrep.c index 4531f11267..857f6ef818 100644 --- a/usr/src/cmd/pgrep/pgrep.c +++ b/usr/src/cmd/pgrep/pgrep.c @@ -597,6 +597,8 @@ main(int argc, char *argv[]) const char *optstr; optdesc_t *optd; int nmatches, c; + const char *zroot; + char buf[PATH_MAX]; DIR *dirp; @@ -626,6 +628,12 @@ main(int argc, char *argv[]) opterr = 0; + zroot = zone_get_nroot(); + if (zroot != NULL) { + (void) snprintf(buf, sizeof (buf), "%s/%s", zroot, g_procdir); + g_procdir = buf; + } + while (optind < argc) { while ((c = getopt(argc, argv, optstr)) != (int)EOF) { diff --git a/usr/src/cmd/prstat/prstat.c b/usr/src/cmd/prstat/prstat.c index fc16e435f6..982c860d0d 100644 --- a/usr/src/cmd/prstat/prstat.c +++ b/usr/src/cmd/prstat/prstat.c @@ -26,6 +26,7 @@ * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier + * Copyright 2012 Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -181,6 +182,33 @@ static optdesc_t opts = { -1 /* sort in decreasing order */ }; + +static int +proc_snprintf(char *_RESTRICT_KYWD s, size_t n, + const char *_RESTRICT_KYWD fmt, ...) +{ + static boolean_t ptools_zroot_valid = B_FALSE; + static const char *ptools_zroot = NULL; + va_list args; + int ret, nret = 0; + + if (ptools_zroot_valid == B_FALSE) { + ptools_zroot_valid = B_TRUE; + ptools_zroot = zone_get_nroot(); + } + + if (ptools_zroot != NULL) { + nret = snprintf(s, n, "%s", ptools_zroot); + if (nret > n) + return (nret); + } + va_start(args, fmt); + ret = vsnprintf(s + nret, n - nret, fmt, args); + va_end(args); + + return (ret + nret); +} + /* * Print timestamp as decimal reprentation of time_t value (-d u was specified) * or the standard date format (-d d was specified). @@ -236,7 +264,12 @@ list_getsize(list_t *list) size_t i; uint_t flags = 0; int ret; - size_t physmem = sysconf(_SC_PHYS_PAGES) * pagesize; + size_t physmem; + + if (!(opts.o_outpmode & OPT_VMUSAGE)) + return; + + physmem = sysconf(_SC_PHYS_PAGES) * pagesize; /* * Determine what swap/rss results to calculate. getvmusage() will @@ -848,9 +881,9 @@ lwp_update(lwp_info_t *lwp, pid_t pid, id_t lwpid, struct prusage *usage) static int read_procfile(fd_t **fd, char *pidstr, char *file, void *buf, size_t bufsize) { - char procfile[MAX_PROCFS_PATH]; + char procfile[PATH_MAX]; - (void) snprintf(procfile, MAX_PROCFS_PATH, + (void) proc_snprintf(procfile, PATH_MAX, "/proc/%s/%s", pidstr, file); if ((*fd = fd_open(procfile, O_RDONLY, *fd)) == NULL) return (1); @@ -1376,6 +1409,7 @@ main(int argc, char **argv) int timeout; struct pollfd pollset; char key; + char procpath[PATH_MAX]; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); @@ -1386,7 +1420,7 @@ main(int argc, char **argv) pagesize = sysconf(_SC_PAGESIZE); while ((opt = getopt(argc, argv, - "vcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJWz:Z")) != (int)EOF) { + "vVcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJWz:Z")) != (int)EOF) { switch (opt) { case 'r': opts.o_outpmode |= OPT_NORESOLVE; @@ -1466,6 +1500,9 @@ main(int argc, char **argv) while (p = strtok(NULL, ", ")) add_uid(&ruid_tbl, p); break; + case 'V': + opts.o_outpmode |= OPT_VMUSAGE; + break; case 'p': fill_table(&pid_tbl, optarg, 'p'); break; @@ -1575,7 +1612,8 @@ main(int argc, char **argv) list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS); if (opts.o_outpmode & OPT_TERMCAP) curses_on(); - if ((procdir = opendir("/proc")) == NULL) + (void) proc_snprintf(procpath, sizeof (procpath), "/proc"); + if ((procdir = opendir(procpath)) == NULL) Die(gettext("cannot open /proc directory\n")); if (opts.o_outpmode & OPT_TTY) { (void) printf(gettext("Please wait...\r")); diff --git a/usr/src/cmd/prstat/prstat.h b/usr/src/cmd/prstat/prstat.h index 293123c5b9..bf38b3e2bd 100644 --- a/usr/src/cmd/prstat/prstat.h +++ b/usr/src/cmd/prstat/prstat.h @@ -26,6 +26,7 @@ * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier + * Copyright 2012 Joyent, Inc. All rights reserved. */ #ifndef _PRSTAT_H @@ -72,6 +73,7 @@ extern "C" { #define OPT_ZONES 0x2000 /* report about zones */ #define OPT_PSETS 0x4000 /* report for specified psets */ #define OPT_LGRP 0x8000 /* report home lgroups */ +#define OPT_VMUSAGE 0x10000 /* print accurate, but expensive RSS */ #define OPT_UDATE 0x20000 /* print unix timestamp */ #define OPT_DDATE 0x40000 /* print timestamp in date(1) format */ #define OPT_NORESOLVE 0x80000 /* no nsswitch lookups */ diff --git a/usr/src/cmd/prstat/prutil.c b/usr/src/cmd/prstat/prutil.c index 0f9cbd6c4d..7def1bd40b 100644 --- a/usr/src/cmd/prstat/prutil.c +++ b/usr/src/cmd/prstat/prutil.c @@ -25,6 +25,7 @@ * Use is subject to license terms. * * Portions Copyright 2009 Chad Mynhier + * Copyright 2012 Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -108,7 +109,7 @@ void Usage() { (void) fprintf(stderr, gettext( - "Usage:\tprstat [-acHJLmrRtTvWZ] [-u euidlist] [-U uidlist]\n" + "Usage:\tprstat [-acHJLmrRtTvVWZ] [-u euidlist] [-U uidlist]\n" "\t[-p pidlist] [-P cpulist] [-C psrsetlist] [-h lgrouplist]\n" "\t[-j projidlist] [-k taskidlist] [-z zoneidlist]\n" "\t[-s key | -S key] [-n nprocs[,nusers]] [-d d|u]\n" diff --git a/usr/src/cmd/prtconf/prtconf.c b/usr/src/cmd/prtconf/prtconf.c index 551144976e..0982f1d40b 100644 --- a/usr/src/cmd/prtconf/prtconf.c +++ b/usr/src/cmd/prtconf/prtconf.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2011, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -168,7 +169,7 @@ cleanup_path(const char *input_path, char *path) #ifdef DEBUG static const char *optstring = "abcdDvVxpPFf:M:dLuC"; #else -static const char *optstring = "abcdDvVxpPFf:uC"; +static const char *optstring = "abcdDvVxmpPFf:uC"; #endif /* DEBUG */ int @@ -201,6 +202,9 @@ main(int argc, char *argv[]) case 'v': ++opts.o_verbose; break; + case 'm': + ++opts.o_memory; + break; case 'p': ++opts.o_prominfo; break; @@ -338,34 +342,42 @@ main(int argc, char *argv[]) return (0); } - ret = sysinfo(SI_HW_PROVIDER, hw_provider, sizeof (hw_provider)); - /* - * If 0 bytes are returned (the system returns '1', for the \0), - * we're probably on x86, default to Oracle. - */ - if (ret <= 1) { - (void) strncpy(hw_provider, "Oracle Corporation", + if (!opts.o_memory) { + ret = sysinfo(SI_HW_PROVIDER, hw_provider, sizeof (hw_provider)); + /* + * If 0 bytes are returned (the system returns '1', for the \0), + * we're probably on x86, and there has been no si-hw-provider + * set in /etc/bootrc, default to Joyent. + */ + if (ret <= 1) { + (void) strncpy(hw_provider, "Joyent", + sizeof (hw_provider)); + } + (void) printf("System Configuration: %s %s\n", hw_provider, + opts.o_uts.machine); } - (void) printf("System Configuration: %s %s\n", hw_provider, - opts.o_uts.machine); pagesize = sysconf(_SC_PAGESIZE); npages = sysconf(_SC_PHYS_PAGES); - (void) printf("Memory size: "); - if (pagesize == -1 || npages == -1) - (void) printf("unable to determine\n"); - else { - const int64_t kbyte = 1024; + if (pagesize == -1 || npages == -1) { + if (opts.o_memory) { + (void) printf("0\n"); + return (1); + } else { + (void) printf("Memory size: unable to determine\n"); + } + } else { const int64_t mbyte = 1024 * 1024; int64_t ii = (int64_t)pagesize * npages; - if (ii >= mbyte) - (void) printf("%ld Megabytes\n", + if (opts.o_memory) { + (void) printf("%ld\n", (long)((ii+mbyte-1) / mbyte)); + return (0); + } else { + (void) printf("Memory size: %ld Megabytes\n", (long)((ii+mbyte-1) / mbyte)); - else - (void) printf("%ld Kilobytes\n", - (long)((ii+kbyte-1) / kbyte)); + } } if (opts.o_prominfo) { diff --git a/usr/src/cmd/prtconf/prtconf.h b/usr/src/cmd/prtconf/prtconf.h index 5f9abe968c..0284cc8af1 100644 --- a/usr/src/cmd/prtconf/prtconf.h +++ b/usr/src/cmd/prtconf/prtconf.h @@ -54,6 +54,7 @@ struct prt_opts { int o_drv_name; int o_pseudodevs; int o_fbname; + int o_memory; int o_noheader; int o_prominfo; int o_productinfo; diff --git a/usr/src/cmd/ptools/Makefile.bld b/usr/src/cmd/ptools/Makefile.bld index f5b50f5ea1..e8bb27b043 100644 --- a/usr/src/cmd/ptools/Makefile.bld +++ b/usr/src/cmd/ptools/Makefile.bld @@ -26,6 +26,8 @@ PROG:sh = basename `cd ..; pwd` +include ../../../Makefile.ctf + OBJS = $(PROG).o SRCS = ../$(PROG).c @@ -69,6 +71,12 @@ CERRWARN_pargs += -_gcc=-Wno-type-limits CERRWARN += $(CERRWARN_$(PROG)) +# +# Common code definitions +# +COBJS = ptools_common.o +CINC = -I../../common + # pargs depends on ../../common/elfcap components # pmadvise depends on pmap components @@ -79,14 +87,32 @@ CPPFLAGS_pargs = -I$(ELFCAP) OBJS_pargs = elfcap.o SRCS_pargs = $(ELFCAP)/elfcap.c -CPPFLAGS_pmap = -I$(PMAP) -OBJS_pmap = pmap_common.o +CPPFLAGS_pmap = -I$(PMAP) $(CINC) +OBJS_pmap = pmap_common.o $(COBJS) SRCS_pmap = $(PMAP)/pmap_common.c -CPPFLAGS_pmadvise = -I$(PMAP) -OBJS_pmadvise = pmap_common.o +CPPFLAGS_pmadvise = -I$(PMAP) $(CINC) +OBJS_pmadvise = pmap_common.o $(COBJS) SRCS_pmadvise = $(PMAP)/pmap_common.c +CPPFLAGS_preap = $(CINC) +OBJS_preap = $(COBJS) + +CPPFLAGS_psig = $(CINC) +OBJS_psig = $(COBJS) + +CPPFLAGS_ptime = $(CINC) +OBJS_ptime = $(COBJS) + +CPPFLAGS_ptree = $(CINC) +OBJS_ptree = $(COBJS) + +CPPFLAGS_pwait = $(CINC) +OBJS_pwait = $(COBJS) + +CPPFLAGS_pwdx = $(CINC) +OBJS_pwdx = $(COBJS) + CPPFLAGS += $(CPPFLAGS_$(PROG)) OBJS += $(OBJS_$(PROG)) SRCS += $(SRCS_$(PROG)) @@ -99,12 +125,19 @@ INSTALL_LEGACY=$(RM) $(ROOTPROCBINSYMLINK) ; \ elfcap.o: $(ELFCAP)/elfcap.c $(COMPILE.c) -o $@ $(ELFCAP)/elfcap.c + $(POST_PROCESS_O) pmap_common.o: $(PMAP)/pmap_common.c $(COMPILE.c) -o $@ $(PMAP)/pmap_common.c + $(POST_PROCESS_O) %.o: ../%.c $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.o: ../../common/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) all: $(PROG) diff --git a/usr/src/cmd/ptools/common/ptools_common.c b/usr/src/cmd/ptools/common/ptools_common.c new file mode 100644 index 0000000000..a747ab213e --- /dev/null +++ b/usr/src/cmd/ptools/common/ptools_common.c @@ -0,0 +1,50 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#include <sys/feature_tests.h> +#include <stdio.h> +#include <stdarg.h> +#include <sys/types.h> +#include <zone.h> + +/* + * Common routines for ptools. + */ + +int +proc_snprintf(char *_RESTRICT_KYWD s, size_t n, + const char *_RESTRICT_KYWD fmt, ...) +{ + static boolean_t ptools_zroot_valid = B_FALSE; + static const char *ptools_zroot = NULL; + va_list args; + int ret, nret = 0; + + if (ptools_zroot_valid == B_FALSE) { + ptools_zroot_valid = B_TRUE; + ptools_zroot = zone_get_nroot(); + } + + if (ptools_zroot != NULL) { + nret = snprintf(s, n, "%s", ptools_zroot); + if (nret > n) + return (nret); + } + va_start(args, fmt); + ret = vsnprintf(s + nret, n - nret, fmt, args); + va_end(args); + + return (ret + nret); +} diff --git a/usr/src/cmd/ptools/common/ptools_common.h b/usr/src/cmd/ptools/common/ptools_common.h new file mode 100644 index 0000000000..52bcae9e51 --- /dev/null +++ b/usr/src/cmd/ptools/common/ptools_common.h @@ -0,0 +1,36 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#ifndef _PTOOLS_COMMON_H +#define _PTOOLS_COMMON_H + +#include <sys/feature_tests.h> + +/* + * Common functions for the ptools. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int proc_snprintf(char *_RESTRICT_KYWD, size_t, + const char *_RESTRICT_KYWD, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _PTOOLS_COMMON_H */ diff --git a/usr/src/cmd/ptools/pargs/pargs.c b/usr/src/cmd/ptools/pargs/pargs.c index a0a4febcd4..c65dff56f1 100644 --- a/usr/src/cmd/ptools/pargs/pargs.c +++ b/usr/src/cmd/ptools/pargs/pargs.c @@ -23,7 +23,7 @@ * Use is subject to license terms. */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -791,6 +791,7 @@ static struct aux_id aux_arr[] = { { AT_BASE, "AT_BASE", at_null }, { AT_FLAGS, "AT_FLAGS", at_null }, { AT_ENTRY, "AT_ENTRY", at_null }, + { AT_RANDOM, "AT_RANDOM", at_null }, { AT_SUN_UID, "AT_SUN_UID", at_uid }, { AT_SUN_RUID, "AT_SUN_RUID", at_uid }, { AT_SUN_GID, "AT_SUN_GID", at_gid }, @@ -810,9 +811,11 @@ static struct aux_id aux_arr[] = { { AT_SUN_AUXFLAGS, "AT_SUN_AUXFLAGS", at_flags }, { AT_SUN_EMULATOR, "AT_SUN_EMULATOR", at_str }, { AT_SUN_BRANDNAME, "AT_SUN_BRANDNAME", at_str }, + { AT_SUN_BRAND_NROOT, "AT_SUN_BRAND_NROOT", at_str }, { AT_SUN_BRAND_AUX1, "AT_SUN_BRAND_AUX1", at_null }, { AT_SUN_BRAND_AUX2, "AT_SUN_BRAND_AUX2", at_null }, - { AT_SUN_BRAND_AUX3, "AT_SUN_BRAND_AUX3", at_null } + { AT_SUN_BRAND_AUX3, "AT_SUN_BRAND_AUX3", at_null }, + { AT_SUN_BRAND_AUX4, "AT_SUN_BRAND_AUX4", at_null } }; #define N_AT_ENTS (sizeof (aux_arr) / sizeof (struct aux_id)) diff --git a/usr/src/cmd/ptools/pfiles/pfiles.c b/usr/src/cmd/ptools/pfiles/pfiles.c index fbe54fc0dd..8e09ef534b 100644 --- a/usr/src/cmd/ptools/pfiles/pfiles.c +++ b/usr/src/cmd/ptools/pfiles/pfiles.c @@ -24,7 +24,7 @@ * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. */ /* - * Copyright (c) 2013 Joyent, Inc. All Rights reserved. + * Copyright (c) 2014 Joyent, Inc. All Rights reserved. */ #include <stdio.h> @@ -271,15 +271,14 @@ show_file(void *data, prfdinfo_t *info) (mode & S_IFMT) == S_IFDOOR); if (Pstate(Pr) != PS_DEAD) { - char *dev; + char *dev = NULL; if ((mode & S_IFMT) == S_IFSOCK) dosocket(Pr, info->pr_fd); else if ((mode & S_IFMT) == S_IFIFO) dofifo(Pr, info->pr_fd); - if ((mode & S_IFMT) == S_IFCHR && - (dev = strrchr(info->pr_path, ':')) != NULL) { + if ((mode & S_IFMT) == S_IFCHR) { /* * There's no elegant way to determine * if a character device supports TLI, @@ -291,11 +290,20 @@ show_file(void *data, prfdinfo_t *info) "tcp", "tcp6", "udp", "udp6", NULL }; - dev++; /* skip past the `:' */ - for (i = 0; tlidevs[i] != NULL; i++) { - if (strcmp(dev, tlidevs[i]) == 0) { - dotli(Pr, info->pr_fd); - break; + /* global zone: /devices paths */ + dev = strrchr(info->pr_path, ':'); + /* also check the /dev path for zones */ + if (dev == NULL) + dev = strrchr(info->pr_path, '/'); + if (dev != NULL) { + dev++; /* skip past the `:' */ + + for (i = 0; tlidevs[i] != NULL; i++) { + if (strcmp(dev, tlidevs[i]) == + 0) { + dotli(Pr, info->pr_fd); + break; + } } } } @@ -549,6 +557,7 @@ show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len) case AF_IPX: p = "AF_IPX"; break; case AF_ROUTE: p = "AF_ROUTE"; break; case AF_LINK: p = "AF_LINK"; break; + case AF_LX_NETLINK: p = "AF_LX_NETLINK"; break; } (void) printf("\t%s: %s\n", str, p); @@ -796,9 +805,11 @@ dotli(struct ps_prochandle *Pr, int fd) strcmd.sc_cmd = TI_GETMYNAME; if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0) - show_sockaddr("sockname", (void *)&strcmd.sc_buf, 0); + show_sockaddr("sockname", (void *)&strcmd.sc_buf, + (size_t)strcmd.sc_len); strcmd.sc_cmd = TI_GETPEERNAME; if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0) - show_sockaddr("peername", (void *)&strcmd.sc_buf, 0); + show_sockaddr("peername", (void *)&strcmd.sc_buf, + (size_t)strcmd.sc_len); } diff --git a/usr/src/cmd/ptools/pflags/pflags.c b/usr/src/cmd/ptools/pflags/pflags.c index 8054a80d3c..f19a945d95 100644 --- a/usr/src/cmd/ptools/pflags/pflags.c +++ b/usr/src/cmd/ptools/pflags/pflags.c @@ -25,7 +25,7 @@ */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2015, Joyent, Inc. */ #include <stdio.h> @@ -469,6 +469,9 @@ prwhy(int why) case PR_SUSPENDED: str = "PR_SUSPENDED"; break; + case PR_BRAND: + str = "PR_BRAND"; + break; default: str = buf; (void) sprintf(str, "%d", why); diff --git a/usr/src/cmd/ptools/pmadvise/pmadvise.c b/usr/src/cmd/ptools/pmadvise/pmadvise.c index 8500a00dc6..6b9623508a 100644 --- a/usr/src/cmd/ptools/pmadvise/pmadvise.c +++ b/usr/src/cmd/ptools/pmadvise/pmadvise.c @@ -25,6 +25,10 @@ */ /* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + +/* * pmadvise * * ptool wrapper for madvise(3C) to apply memory advice to running processes @@ -153,7 +157,7 @@ * Advice that can be passed to madvise fit into three groups that each * contain 3 mutually exclusive options. These groups are defined below: * Group 1: normal, random, sequential - * Group 2: willneed, dontneed, free + * Group 2: willneed, dontneed, free, purge * Group 3: default, accesslwp, accessmany * Thus, advice that includes (at most) one from each group is valid. * @@ -164,7 +168,7 @@ #define GRP1_ADV (1 << MADV_NORMAL | 1 << MADV_RANDOM | \ 1 << MADV_SEQUENTIAL) #define GRP2_ADV (1 << MADV_WILLNEED | 1 << MADV_DONTNEED | \ - 1 << MADV_FREE) + 1 << MADV_FREE | 1 << MADV_PURGE) #define GRP3_ADV (1 << MADV_ACCESS_DEFAULT | 1 << MADV_ACCESS_LWP | \ 1 << MADV_ACCESS_MANY) @@ -346,6 +350,8 @@ get_advice(char *optarg) return (1 << MADV_NORMAL); else if (strcmp(optarg, "free") == 0) return (1 << MADV_FREE); + else if (strcmp(optarg, "purge") == 0) + return (1 << MADV_PURGE); else { (void) fprintf(stderr, gettext("%s: invalid advice: %s\n"), progname, optarg); @@ -676,7 +682,7 @@ apply_advice(saddr_t **advicelist) * with the for loop. */ if (psaddr->adv != NO_ADVICE) { - for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) { + for (i = MADV_NORMAL; i <= MADV_PURGE; i++) { if ((psaddr->adv & (1 << i)) && (pr_madvise(Pr, (caddr_t)psaddr->addr, psaddr->length, i) < 0)) { @@ -898,7 +904,7 @@ advtostr(int adv) *buf = '\0'; if (adv != NO_ADVICE) { - for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) { + for (i = MADV_NORMAL; i <= MADV_PURGE; i++) { if (adv & (1 << i)) { /* * check if it's the first advice entry diff --git a/usr/src/cmd/ptools/pmap/pmap.c b/usr/src/cmd/ptools/pmap/pmap.c index 78bfa6b596..03f8bde791 100644 --- a/usr/src/cmd/ptools/pmap/pmap.c +++ b/usr/src/cmd/ptools/pmap/pmap.c @@ -22,6 +22,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -42,6 +43,7 @@ #include <sys/mman.h> #include <sys/lgrp_user.h> #include <libproc.h> +#include "ptools_common.h" #include "pmap_common.h" @@ -199,7 +201,7 @@ main(int argc, char **argv) const char *bar; struct rlimit rlim; struct stat64 statbuf; - char buf[128]; + char buf[PATH_MAX]; int mapfd; int prg_gflags = PGRAB_RDONLY; int prr_flags = 0; @@ -358,7 +360,7 @@ main(int argc, char **argv) proc_unctrl_psinfo(&psinfo); if (Pstate(Pr) != PS_DEAD) { - (void) snprintf(buf, sizeof (buf), + (void) proc_snprintf(buf, sizeof (buf), "/proc/%d/map", (int)psinfo.pr_pid); if ((mapfd = open(buf, O_RDONLY)) < 0) { (void) fprintf(stderr, "%s: cannot " @@ -590,7 +592,7 @@ rmapping_iter(struct ps_prochandle *Pr, proc_map_f *func, void *cd) prmap_t *prmapp, *pmp; ssize_t n; - (void) snprintf(mapname, sizeof (mapname), + (void) proc_snprintf(mapname, sizeof (mapname), "/proc/%d/rmap", (int)Pstatus(Pr)->pr_pid); if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) { @@ -631,7 +633,7 @@ xmapping_iter(struct ps_prochandle *Pr, proc_xmap_f *func, void *cd, int doswap) prxmap_t *prmapp, *pmp; ssize_t n; - (void) snprintf(mapname, sizeof (mapname), + (void) proc_snprintf(mapname, sizeof (mapname), "/proc/%d/xmap", (int)Pstatus(Pr)->pr_pid); if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) { diff --git a/usr/src/cmd/ptools/pmap/pmap_common.c b/usr/src/cmd/ptools/pmap/pmap_common.c index fff55ffdbc..81f42d67f7 100644 --- a/usr/src/cmd/ptools/pmap/pmap_common.c +++ b/usr/src/cmd/ptools/pmap/pmap_common.c @@ -37,6 +37,7 @@ #include <sys/types.h> #include "pmap_common.h" +#include "ptools_common.h" /* * We compare the high memory addresses since stacks are faulted in from @@ -88,7 +89,7 @@ make_name(struct ps_prochandle *Pr, int lflag, uintptr_t addr, return (NULL); /* first see if we can find a path via /proc */ - (void) snprintf(path, sizeof (path), "/proc/%d/path/%s", + (void) proc_snprintf(path, sizeof (path), "/proc/%d/path/%s", (int)Psp->pr_pid, mapname); len = readlink(path, buf, bufsz - 1); if (len >= 0) { @@ -97,7 +98,7 @@ make_name(struct ps_prochandle *Pr, int lflag, uintptr_t addr, } /* fall back to object information reported by /proc */ - (void) snprintf(path, sizeof (path), + (void) proc_snprintf(path, sizeof (path), "/proc/%d/object/%s", (int)Psp->pr_pid, mapname); if (stat(path, &statb) == 0) { dev_t dev = statb.st_dev; diff --git a/usr/src/cmd/ptools/preap/preap.c b/usr/src/cmd/ptools/preap/preap.c index 8d30b8027c..6d8eb75611 100644 --- a/usr/src/cmd/ptools/preap/preap.c +++ b/usr/src/cmd/ptools/preap/preap.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -37,6 +35,8 @@ #include <sys/types.h> #include <sys/wait.h> #include <libproc.h> +#include <limits.h> +#include "ptools_common.h" #define NOREAP_TIME 60 /* wait 60 seconds before allow a reap */ @@ -53,11 +53,11 @@ intr(int sig) static int open_usage(pid_t pid, int *perr) { - char path[64]; + char path[PATH_MAX]; struct stat64 st; int fd; - (void) snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid); + (void) proc_snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid); /* * Attempt to open the usage file, and return the fd if we can diff --git a/usr/src/cmd/ptools/psig/psig.c b/usr/src/cmd/ptools/psig/psig.c index 70af35cb5e..848fda834c 100644 --- a/usr/src/cmd/ptools/psig/psig.c +++ b/usr/src/cmd/ptools/psig/psig.c @@ -21,10 +21,9 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdio_ext.h> #include <stdlib.h> @@ -37,6 +36,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <libproc.h> +#include "ptools_common.h" /* evil knowledge of libc internals */ #include "../../../lib/libc/inc/thr_uberdata.h" @@ -172,7 +172,7 @@ lwp_iter(void *cd, const lwpstatus_t *lwpstatus) static int look(char *arg) { - char pathname[100]; + char pathname[PATH_MAX]; struct stat statb; int fd = -1; int sig, gcode; @@ -199,7 +199,8 @@ look(char *arg) (void) memcpy(&psinfo, psinfop, sizeof (psinfo_t)); proc_unctrl_psinfo(&psinfo); - (void) sprintf(pathname, "/proc/%d/sigact", (int)psinfo.pr_pid); + (void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/sigact", + (int)psinfo.pr_pid); if ((fd = open(pathname, O_RDONLY)) < 0) { perr("open sigact"); goto look_error; @@ -213,8 +214,8 @@ look(char *arg) action = malloc(maxsig * sizeof (struct sigaction)); if (action == NULL) { (void) fprintf(stderr, - "%s: cannot malloc() space for %d sigaction structures\n", - command, maxsig); + "%s: cannot malloc() space for %d sigaction structures\n", + command, maxsig); goto look_error; } if (read(fd, (char *)action, maxsig * sizeof (struct sigaction)) != @@ -239,7 +240,7 @@ look(char *arg) if (psinfo.pr_dmodel != PR_MODEL_NATIVE) { caddr32_t addr; aharraddr = uberaddr + - offsetof(uberdata32_t, siguaction); + offsetof(uberdata32_t, siguaction); aharrlen = sizeof (siguaction32_t) * NSIG; (void) Pread(Pr, &addr, sizeof (addr), uberaddr + offsetof(uberdata32_t, sigacthandler)); @@ -248,7 +249,7 @@ look(char *arg) #endif { aharraddr = uberaddr + - offsetof(uberdata_t, siguaction); + offsetof(uberdata_t, siguaction); aharrlen = sizeof (siguaction_t) * NSIG; (void) Pread(Pr, &intfnaddr, sizeof (intfnaddr), uberaddr + offsetof(uberdata_t, sigacthandler)); @@ -323,7 +324,7 @@ look(char *arg) (void) printf("\t%-8s", hname); else (void) printf("\t0x%-8lx", - (ulong_t)haddr); + (ulong_t)haddr); s = sigflags(sig, sp->sa_flags); (void) printf("%s", (*s != '\0')? s : "\t0"); @@ -334,7 +335,7 @@ look(char *arg) } } else if (sig == SIGCLD) { s = sigflags(sig, - sp->sa_flags & (SA_NOCLDWAIT|SA_NOCLDSTOP)); + sp->sa_flags & (SA_NOCLDWAIT|SA_NOCLDSTOP)); if (*s != '\0') (void) printf("\t\t%s", s); } @@ -371,7 +372,7 @@ sigflags(int sig, int flags) static char code_buf[100]; char *str = code_buf; int flagmask = - (SA_ONSTACK|SA_RESETHAND|SA_RESTART|SA_SIGINFO|SA_NODEFER); + (SA_ONSTACK|SA_RESETHAND|SA_RESTART|SA_SIGINFO|SA_NODEFER); if (sig == SIGCLD) flagmask |= (SA_NOCLDSTOP|SA_NOCLDWAIT); @@ -414,19 +415,19 @@ deinterpose(int sig, void *aharr, psinfo_t *psinfo, struct sigaction *sp) #ifdef _LP64 if (psinfo->pr_dmodel != PR_MODEL_NATIVE) { struct sigaction32 *sa32 = (struct sigaction32 *) - ((uintptr_t)aharr + sig * sizeof (siguaction32_t) + - offsetof(siguaction32_t, sig_uaction)); + ((uintptr_t)aharr + sig * sizeof (siguaction32_t) + + offsetof(siguaction32_t, sig_uaction)); sp->sa_flags = sa32->sa_flags; sp->sa_handler = (void (*)())(uintptr_t)sa32->sa_handler; (void) memcpy(&sp->sa_mask, &sa32->sa_mask, - sizeof (sp->sa_mask)); + sizeof (sp->sa_mask)); } else #endif { struct sigaction *sa = (struct sigaction *) - ((uintptr_t)aharr + sig * sizeof (siguaction_t) + - offsetof(siguaction_t, sig_uaction)); + ((uintptr_t)aharr + sig * sizeof (siguaction_t) + + offsetof(siguaction_t, sig_uaction)); sp->sa_flags = sa->sa_flags; sp->sa_handler = sa->sa_handler; diff --git a/usr/src/cmd/ptools/ptime/ptime.c b/usr/src/cmd/ptools/ptime/ptime.c index bc862cddb8..7c9be226e1 100644 --- a/usr/src/cmd/ptools/ptime/ptime.c +++ b/usr/src/cmd/ptools/ptime/ptime.c @@ -24,6 +24,9 @@ * * Portions Copyright 2008 Chad Mynhier */ +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ #include <stdio.h> #include <stdlib.h> @@ -38,6 +41,8 @@ #include <sys/time.h> #include <signal.h> #include <libproc.h> +#include <limits.h> +#include "ptools_common.h" static int look(pid_t); static void hr_min_sec(char *, long); @@ -55,16 +60,39 @@ static char procname[64]; static int Fflag; static int mflag; static int errflg; +static int pflag; + +static int +ptime_pid(const char *pidstr) +{ + struct ps_prochandle *Pr; + pid_t pid; + int gret; + + if ((Pr = proc_arg_grab(pidstr, PR_ARG_PIDS, + Fflag | PGRAB_RDONLY, &gret)) == NULL) { + (void) fprintf(stderr, "%s: cannot examine %s: %s\n", + command, pidstr, Pgrab_error(gret)); + return (1); + } + + pid = Pstatus(Pr)->pr_pid; + (void) sprintf(procname, "%d", (int)pid); /* for perr() */ + (void) look(pid); + Prelease(Pr, 0); + return (0); +} int main(int argc, char **argv) { - int opt; + int opt, exit; pid_t pid; struct siginfo info; int status; int gret; struct ps_prochandle *Pr; + char *pp, *np; if ((command = strrchr(argv[0], '/')) != NULL) command++; @@ -80,6 +108,7 @@ main(int argc, char **argv) mflag = 1; break; case 'p': + pflag = 1; pidarg = optarg; break; default: @@ -93,69 +122,76 @@ main(int argc, char **argv) if (((pidarg != NULL) ^ (argc < 1)) || errflg) { (void) fprintf(stderr, - "usage:\t%s [-mh] [-p pid | command [ args ... ]]\n", + "usage:\t%s [-mh] [-p pidlist | command [ args ... ]]\n", command); (void) fprintf(stderr, " (time a command using microstate accounting)\n"); return (1); } - if (pidarg != NULL) { - if ((Pr = proc_arg_grab(pidarg, PR_ARG_PIDS, - Fflag | PGRAB_RDONLY, &gret)) == NULL) { - (void) fprintf(stderr, "%s: cannot examine %s: %s\n", - command, pidarg, Pgrab_error(gret)); - return (1); - } - } else { - if ((Pr = Pcreate(argv[0], &argv[0], &gret, NULL, 0)) == NULL) { - (void) fprintf(stderr, "%s: failed to exec %s: %s\n", - command, argv[0], Pcreate_error(gret)); - return (1); - } - if (Psetrun(Pr, 0, 0) == -1) { - (void) fprintf(stderr, "%s: failed to set running %s: " - "%s\n", command, argv[0], strerror(errno)); - return (1); + if (pflag) { + exit = 0; + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + pp = pidarg; + if ((np = strchr(pp, ' ')) != NULL || + (np = strchr(pp, ',')) != NULL) + pflag++; + while (np != NULL) { + *np = '\0'; + exit |= ptime_pid(pp); + pp = np + 1; + np = strchr(pp, ' '); + if (np == NULL) + np = strchr(pp, ','); } + exit |= ptime_pid(pp); + return (exit); + } + + + if ((Pr = Pcreate(argv[0], &argv[0], &gret, NULL, 0)) == NULL) { + (void) fprintf(stderr, "%s: failed to exec %s: %s\n", + command, argv[0], Pcreate_error(gret)); + return (1); + } + if (Psetrun(Pr, 0, 0) == -1) { + (void) fprintf(stderr, "%s: failed to set running %s: " + "%s\n", command, argv[0], strerror(errno)); + return (1); } pid = Pstatus(Pr)->pr_pid; + (void) sprintf(procname, "%d", (int)pid); /* for perr() */ (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); - if (pidarg == NULL) - (void) waitid(P_PID, pid, &info, WEXITED | WNOWAIT); + (void) waitid(P_PID, pid, &info, WEXITED | WNOWAIT); (void) look(pid); - if (pidarg != NULL) { - Prelease(Pr, 0); - return (0); - } else { - (void) waitpid(pid, &status, 0); + (void) waitpid(pid, &status, 0); - if (WIFEXITED(status)) - return (WEXITSTATUS(status)); + if (WIFEXITED(status)) + return (WEXITSTATUS(status)); - if (WIFSIGNALED(status)) { - int sig = WTERMSIG(status); - char name[SIG2STR_MAX]; + if (WIFSIGNALED(status)) { + int sig = WTERMSIG(status); + char name[SIG2STR_MAX]; - (void) fprintf(stderr, "%s: command terminated " - "abnormally by %s\n", command, - proc_signame(sig, name, sizeof (name))); - } - - return (status | WCOREFLG); /* see time(1) */ + (void) fprintf(stderr, "%s: command terminated " + "abnormally by %s\n", command, + proc_signame(sig, name, sizeof (name))); } + + return (status | WCOREFLG); /* see time(1) */ } static int look(pid_t pid) { - char pathname[100]; + char pathname[PATH_MAX]; int rval = 0; int fd; psinfo_t psinfo; @@ -167,7 +203,8 @@ look(pid_t pid) if (proc_get_psinfo(pid, &psinfo) < 0) return (perr("read psinfo")); - (void) sprintf(pathname, "/proc/%d/usage", (int)pid); + (void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/usage", + (int)pid); if ((fd = open(pathname, O_RDONLY)) < 0) return (perr("open usage")); @@ -187,6 +224,9 @@ look(pid_t pid) tsadd(&sys, &sys, &pup->pr_ttime); (void) fprintf(stderr, "\n"); + if (pflag > 1) + (void) fprintf(stderr, "%d:\t%.70s\n", + (int)psinfo.pr_pid, psinfo.pr_psargs); prtime("real", &real); prtime("user", &user); prtime("sys", &sys); diff --git a/usr/src/cmd/ptools/ptree/ptree.c b/usr/src/cmd/ptools/ptree/ptree.c index 27fef9b7f0..92a65e2f44 100644 --- a/usr/src/cmd/ptools/ptree/ptree.c +++ b/usr/src/cmd/ptools/ptree/ptree.c @@ -21,14 +21,13 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* * ptree -- print family tree of processes */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <assert.h> #include <stdio.h> #include <string.h> @@ -48,6 +47,7 @@ #include <sys/ctfs.h> #include <libcontract_priv.h> #include <sys/stat.h> +#include "ptools_common.h" #define FAKEDPID0(p) (p->pid == 0 && p->psargs[0] == '\0') @@ -103,10 +103,11 @@ main(int argc, char **argv) char *s; int n; int retc = 0; + char ppath[PATH_MAX]; DIR *dirp; struct dirent *dentp; - char pname[100]; + char pname[PATH_MAX]; int pdlen; ps_t *p; @@ -169,16 +170,18 @@ main(int argc, char **argv) psize = 0; ps = NULL; + (void) proc_snprintf(ppath, sizeof (ppath), "/proc"); + /* * Search the /proc directory for all processes. */ - if ((dirp = opendir("/proc")) == NULL) { - (void) fprintf(stderr, "%s: cannot open /proc directory\n", - command); + if ((dirp = opendir(ppath)) == NULL) { + (void) fprintf(stderr, "%s: cannot open %s directory\n", + command, ppath); return (1); } - (void) strcpy(pname, "/proc"); + (void) strcpy(pname, ppath); pdlen = strlen(pname); pname[pdlen++] = '/'; diff --git a/usr/src/cmd/ptools/pwait/pwait.c b/usr/src/cmd/ptools/pwait/pwait.c index 0733c355cf..ec11573477 100644 --- a/usr/src/cmd/ptools/pwait/pwait.c +++ b/usr/src/cmd/ptools/pwait/pwait.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdio_ext.h> #include <ctype.h> @@ -39,6 +37,8 @@ #include <poll.h> #include <procfs.h> #include <sys/resource.h> +#include <limits.h> +#include "ptools_common.h" static int count_my_files(); static char *command; @@ -49,6 +49,7 @@ static char *command; int main(int argc, char **argv) { + char buf[PATH_MAX]; unsigned long remain = 0; struct pollfd *pollfd; struct pollfd *pfd; @@ -75,10 +76,12 @@ main(int argc, char **argv) (void) fprintf(stderr, "usage:\t%s [-v] pid ...\n", command); (void) fprintf(stderr, " (wait for processes to terminate)\n"); (void) fprintf(stderr, - " -v: verbose; report terminations to standard out\n"); + " -v: verbose; report terminations to standard out\n"); return (2); } + (void) proc_snprintf(buf, sizeof (buf), "/proc/"); + /* make sure we have enough file descriptors */ if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { int nfiles = count_my_files(); @@ -87,8 +90,8 @@ main(int argc, char **argv) rlim.rlim_cur = argc + nfiles + SLOP; if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { (void) fprintf(stderr, - "%s: insufficient file descriptors\n", - command); + "%s: insufficient file descriptors\n", + command); return (2); } } @@ -108,11 +111,11 @@ main(int argc, char **argv) if (strchr(arg, '/') != NULL) (void) strncpy(psinfofile, arg, sizeof (psinfofile)); else { - (void) strcpy(psinfofile, "/proc/"); + (void) strcpy(psinfofile, buf); (void) strncat(psinfofile, arg, sizeof (psinfofile)-6); } (void) strncat(psinfofile, "/psinfo", - sizeof (psinfofile)-strlen(psinfofile)); + sizeof (psinfofile)-strlen(psinfofile)); pfd = &pollfd[i]; if ((pfd->fd = open(psinfofile, O_RDONLY)) >= 0) { @@ -126,7 +129,7 @@ main(int argc, char **argv) pfd->revents = 0; } else if (errno == ENOENT) { (void) fprintf(stderr, "%s: no such process: %s\n", - command, arg); + command, arg); } else { perror(arg); } @@ -160,9 +163,9 @@ main(int argc, char **argv) if (pread(pfd->fd, &psinfo, sizeof (psinfo), (off_t)0) == sizeof (psinfo)) { - (void) printf( - "%s: terminated, wait status 0x%.4x\n", - arg, psinfo.pr_wstat); + (void) printf("%s: terminated, " + "wait status 0x%.4x\n", + arg, psinfo.pr_wstat); } else { (void) printf( "%s: terminated\n", arg); @@ -170,10 +173,10 @@ main(int argc, char **argv) } if (pfd->revents & POLLNVAL) (void) printf("%s: system process\n", - arg); + arg); if (pfd->revents & ~(POLLPRI|POLLHUP|POLLNVAL)) (void) printf("%s: unknown error\n", - arg); + arg); } (void) close(pfd->fd); diff --git a/usr/src/cmd/ptools/pwdx/pwdx.c b/usr/src/cmd/ptools/pwdx/pwdx.c index adf42c0877..4a2c6f0c3f 100644 --- a/usr/src/cmd/ptools/pwdx/pwdx.c +++ b/usr/src/cmd/ptools/pwdx/pwdx.c @@ -22,10 +22,9 @@ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <unistd.h> #include <string.h> @@ -33,6 +32,8 @@ #include <libproc.h> #include <sys/param.h> +#include "ptools_common.h" + static char *command; static int @@ -49,7 +50,7 @@ show_cwd(const char *arg) return (1); } - (void) snprintf(proc, sizeof (proc), "/proc/%d/path/cwd", + (void) proc_snprintf(proc, sizeof (proc), "/proc/%d/path/cwd", (int)p.pr_pid); if ((ret = readlink(proc, cwd, sizeof (cwd) - 1)) <= 0) { diff --git a/usr/src/cmd/rcap/common/utils.c b/usr/src/cmd/rcap/common/utils.c index 799fdcef23..dd511c7c50 100644 --- a/usr/src/cmd/rcap/common/utils.c +++ b/usr/src/cmd/rcap/common/utils.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <sys/param.h> @@ -257,77 +258,3 @@ xatoi(char *p) return (i); } } - -/* - * get_running_zones() calls zone_list(2) to find out how many zones are - * running. It then calls zone_list(2) again to fetch the list of running - * zones (stored in *zents). - */ -int -get_running_zones(uint_t *nzents, zone_entry_t **zents) -{ - zoneid_t *zids; - uint_t nzents_saved; - int i; - zone_entry_t *zentp; - zone_state_t zstate; - - *zents = NULL; - if (zone_list(NULL, nzents) != 0) { - warn(gettext("could not get zoneid list\n")); - return (E_ERROR); - } - -again: - if (*nzents == 0) - return (E_SUCCESS); - - if ((zids = (zoneid_t *)calloc(*nzents, sizeof (zoneid_t))) == NULL) { - warn(gettext("out of memory: zones will not be capped\n")); - return (E_ERROR); - } - - nzents_saved = *nzents; - - if (zone_list(zids, nzents) != 0) { - warn(gettext("could not get zone list\n")); - free(zids); - return (E_ERROR); - } - if (*nzents != nzents_saved) { - /* list changed, try again */ - free(zids); - goto again; - } - - *zents = calloc(*nzents, sizeof (zone_entry_t)); - if (*zents == NULL) { - warn(gettext("out of memory: zones will not be capped\n")); - free(zids); - return (E_ERROR); - } - - zentp = *zents; - for (i = 0; i < *nzents; i++) { - char name[ZONENAME_MAX]; - - if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { - warn(gettext("could not get name for " - "zoneid %d\n"), zids[i]); - continue; - } - - (void) strlcpy(zentp->zname, name, sizeof (zentp->zname)); - zentp->zid = zids[i]; - if (zone_get_state(name, &zstate) != Z_OK || - zstate != ZONE_STATE_RUNNING) - continue; - - - zentp++; - } - *nzents = zentp - *zents; - - free(zids); - return (E_SUCCESS); -} diff --git a/usr/src/cmd/rcap/common/utils.h b/usr/src/cmd/rcap/common/utils.h index 7196cfb4ce..cf2e17c080 100644 --- a/usr/src/cmd/rcap/common/utils.h +++ b/usr/src/cmd/rcap/common/utils.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #ifndef _UTILS_H @@ -98,7 +99,6 @@ extern void vdprintfe(int, const char *, va_list); extern void dprintfe(int, char *, ...); extern void hrt2ts(hrtime_t, timestruc_t *); extern int xatoi(char *); -extern int get_running_zones(uint_t *, zone_entry_t **); #ifdef __cplusplus } diff --git a/usr/src/cmd/rcap/rcapadm/rcapadm.c b/usr/src/cmd/rcap/rcapadm/rcapadm.c index 92888b2071..b92115469a 100644 --- a/usr/src/cmd/rcap/rcapadm/rcapadm.c +++ b/usr/src/cmd/rcap/rcapadm/rcapadm.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -145,20 +146,29 @@ out: scf_handle_destroy(h); } +static int +set_zone_cap(char *zonename, uint64_t mcap) +{ + char cmd[128 + ZONENAME_MAX]; + + (void) snprintf(cmd, sizeof (cmd), "/usr/bin/prctl -r " + "-n zone.max-physical-memory -v %llu -i zone %s", mcap, zonename); + return (system(cmd)); +} + /* * Update the in-kernel memory cap for the specified zone. */ static int update_zone_mcap(char *zonename, char *maxrss) { - zoneid_t zone_id; uint64_t num; if (getzoneid() != GLOBAL_ZONEID || zonecfg_in_alt_root()) return (E_SUCCESS); /* get the running zone from the kernel */ - if ((zone_id = getzoneidbyname(zonename)) == -1) { + if (getzoneidbyname(zonename) == -1) { (void) fprintf(stderr, gettext("zone '%s' must be running\n"), zonename); return (E_ERROR); @@ -169,7 +179,7 @@ update_zone_mcap(char *zonename, char *maxrss) return (E_ERROR); } - if (zone_setattr(zone_id, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { + if (set_zone_cap(zonename, num) == -1) { (void) fprintf(stderr, gettext("could not set memory " "cap for zone '%s'\n"), zonename); return (E_ERROR); diff --git a/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c b/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c index db86aa6276..88403dda37 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c @@ -21,16 +21,17 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <procfs.h> #include <project.h> #include <stdlib.h> #include <strings.h> #include <zone.h> #include <libzonecfg.h> +#include <dirent.h> +#include <libproc.h> #include "rcapd.h" #include "utils.h" @@ -39,61 +40,117 @@ extern boolean_t gz_capped; /* round up to next y = 2^n */ #define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) -static void -update_zone(zone_entry_t *zent, void *walk_data) +static struct ps_prochandle * +grab_zone_proc(zoneid_t zid) { - void(*update_notification_cb)(char *, char *, int, uint64_t, int) = - (void(*)(char *, char *, int, uint64_t, int))walk_data; - int changes; - int64_t max_rss; + DIR *dirp; + struct dirent *dentp; + int pid, pid_self, tmp; + psinfo_t psinfo; + struct ps_prochandle *pr = NULL; + + pid_self = getpid(); + + if ((dirp = opendir("/proc")) == NULL) + return (NULL); + + while (dentp = readdir(dirp)) { + pid = atoi(dentp->d_name); + + /* Skip self */ + if (pid == pid_self) + continue; + + if (proc_get_psinfo(pid, &psinfo) != 0) + continue; + + if (psinfo.pr_zoneid != zid) + continue; + + /* attempt to grab process */ + if ((pr = Pgrab(pid, 0, &tmp)) != NULL) { + if (Psetflags(pr, PR_RLC) != 0) { + Prelease(pr, 0); + } + if (Pcreate_agent(pr) == 0) { + if (pr_getzoneid(pr) != zid) { + Prelease(pr, 0); + continue; + } + + (void) closedir(dirp); + return (pr); + } else { + Prelease(pr, 0); + } + } + } + + (void) closedir(dirp); + return (NULL); +} + +static uint64_t +get_zone_cap(zoneid_t zid) +{ + rctlblk_t *rblk; uint64_t mcap; - lcollection_t *lcol; - rcid_t colid; + struct ps_prochandle *pr; - if (zone_getattr(zent->zid, ZONE_ATTR_PHYS_MCAP, &mcap, - sizeof (mcap)) != -1 && mcap != 0) - max_rss = ROUNDUP(mcap, 1024) / 1024; - else - max_rss = 0; - - if (zent->zid == GLOBAL_ZONEID) { - if (max_rss > 0) - gz_capped = B_TRUE; - else - gz_capped = B_FALSE; + if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) + return (UINT64_MAX); + + if ((pr = grab_zone_proc(zid)) == NULL) { + free(rblk); + return (UINT64_MAX); } + if (pr_getrctl(pr, "zone.max-physical-memory", NULL, rblk, + RCTL_FIRST)) { + Pdestroy_agent(pr); + Prelease(pr, 0); + free(rblk); + return (UINT64_MAX); + } - colid.rcid_type = RCIDT_ZONE; - colid.rcid_val = zent->zid; + Pdestroy_agent(pr); + Prelease(pr, 0); - lcol = lcollection_insert_update(&colid, max_rss, zent->zname, - &changes); - if (update_notification_cb != NULL) - update_notification_cb("zone", zent->zname, changes, max_rss, - (lcol != NULL) ? lcol->lcol_mark : 0); + mcap = rctlblk_get_value(rblk); + free(rblk); + return (mcap); } - +/* + * For zones, rcapd only caps the global zone, since each non-global zone + * caps itself. + */ /* ARGSUSED */ void lcollection_update_zone(lcollection_update_type_t ut, void(*update_notification_cb)(char *, char *, int, uint64_t, int)) { - int i; - uint_t nzents; - zone_entry_t *zents; - - /* - * Enumerate running zones. - */ - if (get_running_zones(&nzents, &zents) != 0) - return; - - for (i = 0; i < nzents; i++) { - update_zone(&zents[i], (void *)update_notification_cb); + int changes; + int64_t max_rss; + uint64_t mcap; + lcollection_t *lcol; + rcid_t colid; + mcap = get_zone_cap(GLOBAL_ZONEID); + if (mcap != 0 && mcap != UINT64_MAX) { + max_rss = ROUNDUP(mcap, 1024) / 1024; + gz_capped = B_TRUE; + } else { + max_rss = UINT64_MAX / 1024; + gz_capped = B_FALSE; } - free(zents); + colid.rcid_type = RCIDT_ZONE; + colid.rcid_val = GLOBAL_ZONEID; + + lcol = lcollection_insert_update(&colid, max_rss, GLOBAL_ZONENAME, + &changes); + if (update_notification_cb != NULL) + update_notification_cb("zone", GLOBAL_ZONENAME, changes, + max_rss, (lcol != NULL) ? lcol->lcol_mark : 0); } diff --git a/usr/src/cmd/rcap/rcapd/rcapd_scanner.c b/usr/src/cmd/rcap/rcapd/rcapd_scanner.c index b39811b552..254bb9e922 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_scanner.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_scanner.c @@ -21,6 +21,7 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2012 Joyent, Inc. All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -551,7 +552,7 @@ pageout(pid_t pid, struct ps_prochandle *Pr, caddr_t start, caddr_t end) errno = 0; res = pr_memcntl(Pr, start, (end - start), MC_SYNC, - (caddr_t)(MS_ASYNC | MS_INVALIDATE), 0, 0); + (caddr_t)(MS_ASYNC | MS_INVALCURPROC), 0, 0); debug_high("pr_memcntl [%p-%p): %d", (void *)start, (void *)end, res); /* diff --git a/usr/src/cmd/rcap/rcapstat/rcapstat.c b/usr/src/cmd/rcap/rcapstat/rcapstat.c index 0632250fed..2838c6e5d5 100644 --- a/usr/src/cmd/rcap/rcapstat/rcapstat.c +++ b/usr/src/cmd/rcap/rcapstat/rcapstat.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -72,6 +73,8 @@ typedef struct col { static col_t *col_head; static int ncol; +#define RCAPD_NA "rcapd is not active (try zonememstat)\n" + static col_t * col_find(rcid_t id) { @@ -152,7 +155,7 @@ read_stats(rcid_type_t stat_type) struct stat st; if ((fd = open(STAT_FILE_DEFAULT, O_RDONLY)) < 0) { - warn(gettext("rcapd is not active\n")); + warn(gettext(RCAPD_NA)); return (E_ERROR); } @@ -173,7 +176,7 @@ read_stats(rcid_type_t stat_type) pid = hdr.rs_pid; (void) snprintf(procfile, 20, "/proc/%lld/psinfo", pid); if ((proc_fd = open(procfile, O_RDONLY)) < 0) { - warn(gettext("rcapd is not active\n")); + warn(gettext(RCAPD_NA)); (void) close(fd); return (E_ERROR); } diff --git a/usr/src/cmd/rcm_daemon/Makefile.com b/usr/src/cmd/rcm_daemon/Makefile.com index 4b12d1cd27..59e6e49371 100644 --- a/usr/src/cmd/rcm_daemon/Makefile.com +++ b/usr/src/cmd/rcm_daemon/Makefile.com @@ -56,7 +56,6 @@ COMMON_MOD_SRC = \ $(COMMON)/pool_rcm.c \ $(COMMON)/mpxio_rcm.c \ $(COMMON)/ip_anon_rcm.c \ - $(COMMON)/svm_rcm.c \ $(COMMON)/bridge_rcm.c sparc_MOD_SRC = $(COMMON)/ttymux_rcm.c @@ -82,7 +81,6 @@ COMMON_MOD_OBJ = \ pool_rcm.o \ mpxio_rcm.o \ ip_anon_rcm.o \ - svm_rcm.o \ bridge_rcm.o sparc_MOD_OBJ = ttymux_rcm.o @@ -103,7 +101,6 @@ COMMON_RCM_MODS = \ SUNW_pool_rcm.so \ SUNW_mpxio_rcm.so \ SUNW_ip_anon_rcm.so \ - SUNW_svm_rcm.so \ SUNW_bridge_rcm.so sparc_RCM_MODS = SUNW_ttymux_rcm.so @@ -132,7 +129,6 @@ LINTFLAGS += -u -erroff=E_FUNC_ARG_UNUSED LDLIBS_MODULES = SUNW_pool_rcm.so := LDLIBS_MODULES += -L$(ROOT)/usr/lib -lpool -SUNW_svm_rcm.so := LDLIBS_MODULES += -L$(ROOT)/usr/lib -lmeta SUNW_network_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_vlan_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_vnic_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c index 746c528347..f57309ec9b 100644 --- a/usr/src/cmd/savecore/savecore.c +++ b/usr/src/cmd/savecore/savecore.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2015, Joyent, Inc. */ /* * Copyright 2012 Nexenta Systems, Inc. All rights reserved. @@ -1929,24 +1929,32 @@ main(int argc, char *argv[]) if (sec < 1) sec = 1; - (void) fprintf(mfile, "[[[[,,,"); - for (i = 0; i < argc; i++) - (void) fprintf(mfile, "%s ", argv[i]); - (void) fprintf(mfile, "\n"); - (void) fprintf(mfile, ",,,%s/%s\n", savedir, corefile); - (void) fprintf(mfile, ",,,%s %s %s %s %s\n", - dumphdr.dump_utsname.sysname, - dumphdr.dump_utsname.nodename, - dumphdr.dump_utsname.release, - dumphdr.dump_utsname.version, - dumphdr.dump_utsname.machine); - (void) fprintf(mfile, "Uncompress pages,%"PRIu64"\n", - saved); - (void) fprintf(mfile, "Uncompress time,%d\n", sec); - (void) fprintf(mfile, "Uncompress pages/sec,%" - PRIu64"\n", saved / sec); - (void) fprintf(mfile, "]]]]\n"); - (void) fclose(mfile); + if (mfile == NULL) { + logprint(SC_SL_WARN, + "Can't create %s: %s", + METRICSFILE, strerror(errno)); + } else { + (void) fprintf(mfile, "[[[[,,,"); + for (i = 0; i < argc; i++) + (void) fprintf(mfile, "%s ", argv[i]); + (void) fprintf(mfile, "\n"); + (void) fprintf(mfile, ",,,%s/%s\n", savedir, + corefile); + (void) fprintf(mfile, ",,,%s %s %s %s %s\n", + dumphdr.dump_utsname.sysname, + dumphdr.dump_utsname.nodename, + dumphdr.dump_utsname.release, + dumphdr.dump_utsname.version, + dumphdr.dump_utsname.machine); + (void) fprintf(mfile, + "Uncompress pages,%"PRIu64"\n", saved); + (void) fprintf(mfile, "Uncompress time,%d\n", + sec); + (void) fprintf(mfile, "Uncompress pages/sec,%" + PRIu64"\n", saved / sec); + (void) fprintf(mfile, "]]]]\n"); + (void) fclose(mfile); + } } } diff --git a/usr/src/cmd/sed/main.c b/usr/src/cmd/sed/main.c index 204583b877..dc3ef02619 100644 --- a/usr/src/cmd/sed/main.c +++ b/usr/src/cmd/sed/main.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.com> * Copyright (c) 2011 Gary Mills - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1992 Diomidis Spinellis. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -42,9 +42,7 @@ #include <err.h> #include <errno.h> #include <fcntl.h> -#include <getopt.h> #include <libgen.h> -#include <libintl.h> #include <limits.h> #include <locale.h> #include <regex.h> @@ -53,6 +51,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libintl.h> #include "defs.h" #include "extern.h" @@ -107,11 +106,6 @@ static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */ static const char *inplace; /* Inplace edit file extension. */ ulong_t linenum; -static const struct option lopts[] = { - {"in-place", optional_argument, NULL, 'i'}, - {NULL, 0, NULL, 0} -}; - static void add_compunit(enum e_cut, char *); static void add_file(char *); static void usage(void); @@ -133,18 +127,14 @@ main(int argc, char *argv[]) fflag = 0; inplace = NULL; - while ((c = getopt_long(argc, argv, "EI::ae:f:i::lnr", lopts, NULL)) != - -1) + while ((c = getopt(argc, argv, "EI:ae:f:i:lnr")) != -1) switch (c) { case 'r': /* Gnu sed compat */ case 'E': rflags = REG_EXTENDED; break; case 'I': - if (optarg != NULL) - inplace = optarg; - else - inplace = ""; + inplace = optarg; ispan = 1; /* span across input files */ break; case 'a': @@ -161,10 +151,7 @@ main(int argc, char *argv[]) add_compunit(CU_FILE, optarg); break; case 'i': - if (optarg != NULL) - inplace = optarg; - else - inplace = ""; + inplace = optarg; ispan = 0; /* don't span across input files */ break; case 'l': @@ -205,8 +192,8 @@ main(int argc, char *argv[]) static void usage(void) { - (void) fputs(_("usage: sed script [-Ealn] [-i[extension]] [file...]\n" - " sed [-Ealn] [-i[extension]] [-e script]... " + (void) fputs(_("usage: sed script [-Ealn] [-i extension] [file...]\n" + " sed [-Ealn] [-i extension] [-e script]... " "[-f script_file]... [file...]\n"), stderr); exit(1); diff --git a/usr/src/cmd/sendmail/src/Makefile b/usr/src/cmd/sendmail/src/Makefile index 14793754fd..9512a7d976 100644 --- a/usr/src/cmd/sendmail/src/Makefile +++ b/usr/src/cmd/sendmail/src/Makefile @@ -46,7 +46,7 @@ LDFLAGS += $(MAPFILES:%=-M%) LDLIBS += ../libsmutil/libsmutil.a ../libsm/libsm.a -lresolv -lsocket \ -lnsl ../db/libdb.a -lldap -lsldap -lwrap -lumem \ - -lssl -lcrypto -lsasl + -lsunw_ssl -lsunw_crypto -lsasl INCPATH= -I. -I../include -I../db diff --git a/usr/src/cmd/sgs/elfdump/Makefile.targ b/usr/src/cmd/sgs/elfdump/Makefile.targ index f4d74fa9c6..971d9dfeb4 100644 --- a/usr/src/cmd/sgs/elfdump/Makefile.targ +++ b/usr/src/cmd/sgs/elfdump/Makefile.targ @@ -78,6 +78,8 @@ delete: package \ install: all $(VAR_SGSBINPROG) $(VAR_SGSCCSLINK) + -$(RM) $(ROOTPROG) + -$(LN) $(ISAEXEC) $(ROOTPROG) .PARALLEL: $(LINTOUT32) $(LINTOUT64) diff --git a/usr/src/cmd/sgs/elfdump/amd64/Makefile b/usr/src/cmd/sgs/elfdump/amd64/Makefile index be0f2c33f6..039edb65b6 100644 --- a/usr/src/cmd/sgs/elfdump/amd64/Makefile +++ b/usr/src/cmd/sgs/elfdump/amd64/Makefile @@ -38,6 +38,8 @@ LINTFLAGS64 += $(VAR_LINTFLAGS64) VAR_SGSBINPROG= $(VAR_SGSBINPROG64) VAR_SGSCCSLINK= $(VAR_SGSCCSLINK64) +install: all $(ROOTPROG64) + include ../Makefile.targ include ../../Makefile.sub.64 diff --git a/usr/src/cmd/sgs/elfdump/i386/Makefile b/usr/src/cmd/sgs/elfdump/i386/Makefile index d3cb302ac1..95390a2899 100644 --- a/usr/src/cmd/sgs/elfdump/i386/Makefile +++ b/usr/src/cmd/sgs/elfdump/i386/Makefile @@ -30,4 +30,6 @@ include ../Makefile.com ARCH = i386 +install: all $(ROOTPROG32) + include ../Makefile.targ diff --git a/usr/src/cmd/sgs/lex/Makefile.targ b/usr/src/cmd/sgs/lex/Makefile.targ index ea1bee3a62..d1e01f71c9 100644 --- a/usr/src/cmd/sgs/lex/Makefile.targ +++ b/usr/src/cmd/sgs/lex/Makefile.targ @@ -102,4 +102,4 @@ $(LINTPOUT): $(SRCS) $(LINT.c) $(LIBSRCS) $(LDLIBS) 2>&1 | tee -a $(LINTPOUT) $(LINTLIB): $(LINTSRCS) - $(LINT.c) -o $(LIBNAME) $(LINTSRCS) + $(LINT.c) -o $(LIBNAME) $(LINTSRCS) > $(LINTOUT) 2>&1 diff --git a/usr/src/cmd/sgs/lex/common/main.c b/usr/src/cmd/sgs/lex/common/main.c index 17ba4808a4..a1fd526cf9 100644 --- a/usr/src/cmd/sgs/lex/common/main.c +++ b/usr/src/cmd/sgs/lex/common/main.c @@ -30,11 +30,15 @@ /* Copyright 1976, Bell Telephone Laboratories, Inc. */ +/* Copyright (c) 2013, joyent, Inc. All rights reserved. */ + #include <string.h> #include "once.h" #include "sgs.h" #include <locale.h> #include <limits.h> +#include <unistd.h> +#include <libgen.h> static wchar_t L_INITIAL[] = {'I', 'N', 'I', 'T', 'I', 'A', 'L', 0}; static void get1core(void); @@ -46,6 +50,25 @@ static void get3core(void); static void free3core(void); #endif +static int +lex_construct_path(char *buf, size_t size, const char *file, int type) +{ + int ret; + char origin[PATH_MAX]; + + if (type != 0) { + ret = readlink("/proc/self/path/a.out", origin, PATH_MAX - 1); + if (ret < 0) + error( + "lex: failed to read origin from /proc\n"); + origin[ret] = '\0'; + return (snprintf(buf, size, "%s/../%s/%s", dirname(origin), + NBASE, file)); + } + + return (snprintf(buf, size, "%s/%s/%s", NPREFIX, NBASE, file)); +} + int main(int argc, char **argv) { @@ -53,6 +76,7 @@ main(int argc, char **argv) int c; char *apath = NULL; char *ypath; + char pathbuf[PATH_MAX]; Boolean eoption = 0, woption = 0; sargv = argv; @@ -224,6 +248,11 @@ main(int argc, char **argv) free3core(); #endif + /* + * Try to find the file relative to $ORIGIN. Note that we don't touch + * antyhing related to -Y. In fact, unfortunately it's always been + * ignored it seems. + */ if (handleeuc) { if (ratfor) error("Ratfor is not supported by -w or -e option."); @@ -232,9 +261,19 @@ main(int argc, char **argv) else ypath = ratfor ? RATNAME : CNAME; - if (apath != NULL) - ypath = strcat(apath, strrchr(ypath, '/')); - fother = fopen(ypath, "r"); + if (apath == NULL) { + (void) lex_construct_path(pathbuf, sizeof (pathbuf), ypath, 1); + fother = fopen(pathbuf, "r"); + if (fother == NULL) { + (void) lex_construct_path(pathbuf, sizeof (pathbuf), + ypath, 0); + fother = fopen(pathbuf, "r"); + } + } else { + apath = strcat(apath, "/"); + ypath = strcat(apath, ypath); + fother = fopen(ypath, "r"); + } if (fother == NULL) error("Lex driver missing, file %s", ypath); while ((i = getc(fother)) != EOF) diff --git a/usr/src/cmd/sgs/lex/common/once.h b/usr/src/cmd/sgs/lex/common/once.h index 014ca00b17..9e4b0e5e00 100644 --- a/usr/src/cmd/sgs/lex/common/once.h +++ b/usr/src/cmd/sgs/lex/common/once.h @@ -26,11 +26,11 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ +/* Copyright (c) 2013, joyent, Inc. All rights reserved. */ + #ifndef _ONCE_H #define _ONCE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include "ldefs.h" /* once.c */ @@ -73,9 +73,11 @@ int peek = '\n'; /* next input character */ CHR *pushptr = pushc; CHR *slptr = slist; -#define CNAME "/usr/share/lib/ccs/ncform" -#define RATNAME "/usr/share/lib/ccs/nrform" -#define EUCNAME "/usr/share/lib/ccs/nceucform" +#define NPREFIX "/usr" +#define NBASE "/share/lib/ccs/" +#define CNAME "ncform" +#define RATNAME "nrform" +#define EUCNAME "nceucform" int ccount = 1; int casecount = 1; diff --git a/usr/src/cmd/sgs/libconv/common/corenote.c b/usr/src/cmd/sgs/libconv/common/corenote.c index eb998bae45..863c3bc917 100644 --- a/usr/src/cmd/sgs/libconv/common/corenote.c +++ b/usr/src/cmd/sgs/libconv/common/corenote.c @@ -25,7 +25,7 @@ */ /* * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -76,7 +76,7 @@ const char * conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, Conv_inv_buf_t *inv_buf) { - static const Msg types_0_22[] = { + static const Msg types_0_25[] = { MSG_AUXV_AT_NULL, MSG_AUXV_AT_IGNORE, MSG_AUXV_AT_EXECFD, MSG_AUXV_AT_PHDR, MSG_AUXV_AT_PHENT, MSG_AUXV_AT_PHNUM, @@ -88,10 +88,11 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, MSG_AUXV_AT_HWCAP, MSG_AUXV_AT_CLKTCK, MSG_AUXV_AT_FPUCW, MSG_AUXV_AT_DCACHEBSIZE, MSG_AUXV_AT_ICACHEBSIZE, MSG_AUXV_AT_UCACHEBSIZE, - MSG_AUXV_AT_IGNOREPPC + MSG_AUXV_AT_IGNOREPPC, MSG_AUXV_AT_SECURE, + MSG_AUXV_AT_BASE_PLATFORM, MSG_AUXV_AT_RANDOM }; - static const conv_ds_msg_t ds_types_0_22 = { - CONV_DS_MSG_INIT(0, types_0_22) }; + static const conv_ds_msg_t ds_types_0_25 = { + CONV_DS_MSG_INIT(0, types_0_25) }; static const Msg types_2000_2011[] = { MSG_AUXV_AT_SUN_UID, MSG_AUXV_AT_SUN_RUID, @@ -104,19 +105,20 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, static const conv_ds_msg_t ds_types_2000_2011 = { CONV_DS_MSG_INIT(2000, types_2000_2011) }; - static const Msg types_2014_2023[] = { + static const Msg types_2014_2024[] = { MSG_AUXV_AT_SUN_EXECNAME, MSG_AUXV_AT_SUN_MMU, MSG_AUXV_AT_SUN_LDDATA, MSG_AUXV_AT_SUN_AUXFLAGS, MSG_AUXV_AT_SUN_EMULATOR, MSG_AUXV_AT_SUN_BRANDNAME, MSG_AUXV_AT_SUN_BRAND_AUX1, MSG_AUXV_AT_SUN_BRAND_AUX2, - MSG_AUXV_AT_SUN_BRAND_AUX3, MSG_AUXV_AT_SUN_HWCAP2 + MSG_AUXV_AT_SUN_BRAND_AUX3, MSG_AUXV_AT_SUN_HWCAP2, + MSG_AUXV_AT_SUN_BRAND_NROOT }; - static const conv_ds_msg_t ds_types_2014_2023 = { - CONV_DS_MSG_INIT(2014, types_2014_2023) }; + static const conv_ds_msg_t ds_types_2014_2024 = { + CONV_DS_MSG_INIT(2014, types_2014_2024) }; static const conv_ds_t *ds[] = { - CONV_DS_ADDR(ds_types_0_22), CONV_DS_ADDR(ds_types_2000_2011), - CONV_DS_ADDR(ds_types_2014_2023), NULL }; + CONV_DS_ADDR(ds_types_0_25), CONV_DS_ADDR(ds_types_2000_2011), + CONV_DS_ADDR(ds_types_2014_2024), NULL }; return (conv_map_ds(ELFOSABI_NONE, EM_NONE, type, ds, fmt_flags, inv_buf)); diff --git a/usr/src/cmd/sgs/libconv/common/corenote.msg b/usr/src/cmd/sgs/libconv/common/corenote.msg index f1cc32dfde..c68b826f72 100644 --- a/usr/src/cmd/sgs/libconv/common/corenote.msg +++ b/usr/src/cmd/sgs/libconv/common/corenote.msg @@ -24,7 +24,7 @@ # Use is subject to license terms. # # Copyright 2012 DEY Storage Systems, Inc. All rights reserved. -# Copyright (c) 2013, Joyent, Inc. All rights reserved. +# Copyright (c) 2014, Joyent, Inc. All rights reserved. # @ MSG_NT_PRSTATUS "[ NT_PRSTATUS ]" @@ -78,6 +78,9 @@ @ MSG_AUXV_AT_ICACHEBSIZE "ICACHEBSIZE" @ MSG_AUXV_AT_UCACHEBSIZE "UCACHEBSIZE" @ MSG_AUXV_AT_IGNOREPPC "IGNOREPPC" +@ MSG_AUXV_AT_SECURE "SECURE" +@ MSG_AUXV_AT_BASE_PLATFORM "BASE_PLATFORM" +@ MSG_AUXV_AT_RANDOM "RANDOM" @ MSG_AUXV_AT_SUN_UID "SUN_UID" @ MSG_AUXV_AT_SUN_RUID "SUN_RUID" @ MSG_AUXV_AT_SUN_GID "SUN_GID" @@ -100,6 +103,7 @@ @ MSG_AUXV_AT_SUN_BRAND_AUX2 "SUN_BRAND_AUX2" @ MSG_AUXV_AT_SUN_BRAND_AUX3 "SUN_BRAND_AUX3" @ MSG_AUXV_AT_SUN_HWCAP2 "SUN_HWCAP2" +@ MSG_AUXV_AT_SUN_BRAND_NROOT "SUN_BRAND_NROOT" @ MSG_CC_CONTENT_STACK "STACK" diff --git a/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg b/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg index fbc595f5f4..5b0ad9533a 100644 --- a/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg +++ b/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg @@ -24,6 +24,10 @@ # Use is subject to license terms. # +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + # Message file for cmd/sgs/librtld_db. @ MSG_ID_LIBRTLD_DB @@ -104,7 +108,7 @@ @ MSG_DB_RDOBJPADE "rtld_db: rd_objpad_enable(padsize=0x%llx)" @ MSG_DB_64BIT_PREFIX "64/" @ MSG_DB_BRAND_HELPERPATH_PREFIX "%s/%s/%s/%s%s_librtld_db.so.1" -@ MSG_DB_BRAND_HELPERPATH "%s/%s/%s%s_librtld_db.so.1" +@ MSG_DB_BRAND_HELPERPATH "%s/%s/%s/%s%s_librtld_db.so.1" @ MSG_DB_HELPERNOOPS "rtld_db: helper lib loaded but ops not preset" @ MSG_DB_HELPERLOADED "rtld_db: helper library loaded for brand \"%s\"" @ MSG_DB_HELPERLOADFAILED "rtld_db: couldn't load brand helper library %s" diff --git a/usr/src/cmd/sgs/librtld_db/common/rd_elf.c b/usr/src/cmd/sgs/librtld_db/common/rd_elf.c index 410b819b30..b3de685b81 100644 --- a/usr/src/cmd/sgs/librtld_db/common/rd_elf.c +++ b/usr/src/cmd/sgs/librtld_db/common/rd_elf.c @@ -23,6 +23,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + #include <stdlib.h> #include <stdio.h> #include <proc_service.h> @@ -38,6 +42,14 @@ #include <sys/param.h> /* + * We want to include zone.h to pull in the prototype for zone_get_nroot(), + * but we need to avoid pulling in <sys/stream.h>, which has a definition + * of M_DATA that conflicts with the ELF-related definition in machdep_*.h. + */ +#define _SYS_STREAM_H +#include <zone.h> + +/* * 64-bit builds are going to compile this module twice, the * second time with _ELF64 defined. These defines should make * all the necessary adjustments to the code. @@ -283,7 +295,9 @@ _rd_reset32(struct rd_agent *rap) * If we are debugging a branded executable, load the appropriate * helper library, and call its initialization routine. Being unable * to load the helper library is not a critical error. (Hopefully - * we'll still be able to access some objects in the target.) + * we'll still be able to access some objects in the target.) Note + * that we pull in the native root here to allow for helper libraries + * to be properly found from within the branded zone. */ ps_pbrandname = (ps_pbrandname_fp_t)dlsym(RTLD_PROBE, "ps_pbrandname"); while ((ps_pbrandname != NULL) && @@ -294,17 +308,23 @@ _rd_reset32(struct rd_agent *rap) isa = MSG_ORIG(MSG_DB_64BIT_PREFIX); #endif /* _LP64 */ - if (rtld_db_helper_path[0] != '\0') + if (rtld_db_helper_path[0] != '\0') { (void) snprintf(brandlib, MAXPATHLEN, MSG_ORIG(MSG_DB_BRAND_HELPERPATH_PREFIX), rtld_db_helper_path, MSG_ORIG(MSG_DB_HELPER_PREFIX), brandname, isa, brandname); - else + } else { + const char *nroot = zone_get_nroot(); + + if (nroot == NULL) + nroot = ""; + (void) snprintf(brandlib, MAXPATHLEN, - MSG_ORIG(MSG_DB_BRAND_HELPERPATH), + MSG_ORIG(MSG_DB_BRAND_HELPERPATH), nroot, MSG_ORIG(MSG_DB_HELPER_PREFIX), brandname, isa, brandname); + } rap->rd_helper.rh_dlhandle = dlopen(brandlib, RTLD_LAZY | RTLD_LOCAL); diff --git a/usr/src/cmd/sgs/rtld/common/_rtld.h b/usr/src/cmd/sgs/rtld/common/_rtld.h index ece14a855e..83b7071471 100644 --- a/usr/src/cmd/sgs/rtld/common/_rtld.h +++ b/usr/src/cmd/sgs/rtld/common/_rtld.h @@ -26,7 +26,7 @@ * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #ifndef __RTLD_H #define __RTLD_H @@ -589,6 +589,8 @@ extern const char *rpl_ldflags; /* replaceable LD_FLAGS string */ extern const char *rpl_libpath; /* replaceable LD_LIBRARY string */ extern Alist *rpl_libdirs; /* and its associated Pdesc list */ extern const char *rpl_preload; /* replaceable LD_PRELOAD string */ +extern const char *rpl_ldtoxic; /* replaceable LD_TOXIC_PATH string */ +extern Alist *rpl_toxdirs; /* and associated Pdesc list */ extern const char *prm_audit; /* permanent LD_AUDIT string */ extern const char *prm_debug; /* permanent LD_DEBUG string */ diff --git a/usr/src/cmd/sgs/rtld/common/analyze.c b/usr/src/cmd/sgs/rtld/common/analyze.c index e14c121f07..df05f21924 100644 --- a/usr/src/cmd/sgs/rtld/common/analyze.c +++ b/usr/src/cmd/sgs/rtld/common/analyze.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -834,6 +835,44 @@ is_so_loaded(Lm_list *lml, const char *name, int *in_nfavl) } /* + * Walk the toxic path list and determine if the object in question has violated + * the toxic path. When evaluating the toxic path we need to ensure that we + * match any path that's a subdirectory of a listed entry. In other words if + * /foo/bar is toxic, something in /foo/bar/baz/ is no good. However, we need to + * ensure that we don't mark /foo/barbaz/ as bad. + */ +static int +is_load_toxic(Lm_list *lml, Rt_map *nlmp) +{ + const char *fpath = PATHNAME(nlmp); + size_t flen = strlen(fpath); + Pdesc *pdp; + Aliste idx; + + for (ALIST_TRAVERSE(rpl_toxdirs, idx, pdp)) { + if (pdp->pd_plen == 0) + continue; + + if (strncmp(pdp->pd_pname, fpath, pdp->pd_plen) == 0) { + if (pdp->pd_pname[pdp->pd_plen-1] != '/') { + /* + * Path didn't end in a /, make sure + * we're at a directory boundary + * nonetheless. + */ + if (flen > pdp->pd_plen && + fpath[pdp->pd_plen] == '/') + return (1); + continue; + } + return (1); + } + } + + return (0); +} + +/* * Tracing is enabled by the LD_TRACE_LOADED_OPTIONS environment variable which * is normally set from ldd(1). For each link map we load, print the load name * and the full pathname of the associated object. @@ -2169,6 +2208,17 @@ load_finish(Lm_list *lml, const char *name, Rt_map *clmp, int nmode, uint_t rdflags; /* + * If this dependency is associated with a toxic path, then we must + * honor the user's request to die. + */ + if (is_load_toxic(lml, nlmp) != 0) { + eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TOXIC_FILE), + PATHNAME(nlmp)); + rtldexit(lml, 1); + + } + + /* * If this dependency is associated with a required version ensure that * the version is present in the loaded file. */ diff --git a/usr/src/cmd/sgs/rtld/common/globals.c b/usr/src/cmd/sgs/rtld/common/globals.c index 5f48fafc5f..2e98768551 100644 --- a/usr/src/cmd/sgs/rtld/common/globals.c +++ b/usr/src/cmd/sgs/rtld/common/globals.c @@ -24,6 +24,7 @@ * All Rights Reserved * * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -132,6 +133,8 @@ const char *rpl_ldflags = NULL; /* replaceable LD_FLAGS string */ const char *rpl_libpath = NULL; /* replaceable LD_LIBRARY_PATH string */ Alist *rpl_libdirs = NULL; /* and associated Pdesc list */ const char *rpl_preload = NULL; /* replaceable LD_PRELOAD string */ +const char *rpl_ldtoxic = NULL; /* replaceable LD_TOXIC string */ +Alist *rpl_toxdirs = NULL; /* and associated Pdesc list */ const char *prm_audit = NULL; /* permanent LD_AUDIT string */ const char *prm_debug = NULL; /* permanent LD_DEBUG string */ diff --git a/usr/src/cmd/sgs/rtld/common/rtld.msg b/usr/src/cmd/sgs/rtld/common/rtld.msg index 97e2841b8f..9309d0c62e 100644 --- a/usr/src/cmd/sgs/rtld/common/rtld.msg +++ b/usr/src/cmd/sgs/rtld/common/rtld.msg @@ -21,6 +21,7 @@ # # Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, Joyent, Inc. All rights reserved. # @ _START_ @@ -99,9 +100,14 @@ @ MSG_SYS_MPROT "%s: mprotect failed: %s" @ MSG_SYS_MMAPANON "mmap anon failed: %s" +# Secure path failures + @ MSG_SEC_OPEN "%s: open failed: No such file in secure directories" @ MSG_SEC_ILLEGAL "%s: open failed: illegal insecure pathname" +# Toxic failures + +@ MSG_TOXIC_FILE "%s: dependency marked as toxic" # Configuration failures @@ -395,6 +401,7 @@ @ MSG_LD_PROFILE_OUTPUT "PROFILE_OUTPUT" @ MSG_LD_SFCAP "SFCAP" @ MSG_LD_SIGNAL "SIGNAL" +@ MSG_LD_TOXICPATH "TOXIC_PATH" @ MSG_LD_TRACE_OBJS "TRACE_LOADED_OBJECTS" @ MSG_LD_TRACE_OBJS_E "TRACE_LOADED_OBJECTS_E" @ MSG_LD_TRACE_OBJS_A "TRACE_LOADED_OBJECTS_A" diff --git a/usr/src/cmd/sgs/rtld/common/setup.c b/usr/src/cmd/sgs/rtld/common/setup.c index 862bb7d61f..98e3ba5d33 100644 --- a/usr/src/cmd/sgs/rtld/common/setup.c +++ b/usr/src/cmd/sgs/rtld/common/setup.c @@ -28,7 +28,7 @@ * All Rights Reserved */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -819,6 +819,14 @@ setup(char **envp, auxv_t *auxv, Word _flags, char *_platform, int _syspagsz, return (0); } + /* + * Initialize our toxic paths + */ + if (rpl_ldtoxic != NULL) { + (void) expand_paths(mlmp, rpl_ldtoxic, &rpl_toxdirs, + AL_CNT_SEARCH, 0, PD_TKN_CAP); + } + #if defined(_ELF64) /* * If this is a 64-bit process, determine whether this process has diff --git a/usr/src/cmd/sgs/rtld/common/util.c b/usr/src/cmd/sgs/rtld/common/util.c index a702b20e78..09cabeb31b 100644 --- a/usr/src/cmd/sgs/rtld/common/util.c +++ b/usr/src/cmd/sgs/rtld/common/util.c @@ -24,6 +24,7 @@ * All Rights Reserved * * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -1428,6 +1429,7 @@ static u_longlong_t cmdisa = 0; /* command line (-e) ISA */ #define ENV_FLG_CAP_FILES 0x0080000000000ULL #define ENV_FLG_DEFERRED 0x0100000000000ULL #define ENV_FLG_NOENVIRON 0x0200000000000ULL +#define ENV_FLG_TOXICPATH 0x0400000000000ULL #define SEL_REPLACE 0x0001 #define SEL_PERMANT 0x0002 @@ -1601,8 +1603,7 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, if ((len == MSG_LD_FLAGS_SIZE) && (strncmp(s1, MSG_ORIG(MSG_LD_FLAGS), MSG_LD_FLAGS_SIZE) == 0)) { select |= SEL_ACT_SPEC_1; - str = (select & SEL_REPLACE) ? &rpl_ldflags : - &prm_ldflags; + str = &rpl_ldflags; variable = ENV_FLG_FLAGS; } } @@ -1813,6 +1814,8 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, * In case an auditor is called, which in turn might exec(2) a * subprocess, this variable is disabled, so that any subprocess * escapes ldd(1) processing. + * + * Also, look for LD_TOXIC_PATH */ else if (*s1 == 'T') { if (((len == MSG_LD_TRACE_OBJS_SIZE) && @@ -1850,7 +1853,13 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, select |= SEL_ACT_LML; val = LML_FLG_TRC_SEARCH; variable = ENV_FLG_TRACE_PTHS; + } else if ((len == MSG_LD_TOXICPATH_SIZE) && (strncmp(s1, + MSG_ORIG(MSG_LD_TOXICPATH), MSG_LD_TOXICPATH_SIZE) == 0)) { + select |= SEL_ACT_SPEC_1; + str = &rpl_ldtoxic; + variable = ENV_FLG_TOXICPATH; } + } /* * LD_UNREF and LD_UNUSED (internal, used by ldd(1)). @@ -1974,7 +1983,8 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, *lmtflags &= ~val; } else if (select & SEL_ACT_SPEC_1) { /* - * variable is either ENV_FLG_FLAGS or ENV_FLG_LIBPATH + * variable is either ENV_FLG_FLAGS, ENV_FLG_LIBPATH, or + * ENV_FLG_TOXICPATH */ if (env_flags & ENV_TYP_NULL) *str = NULL; diff --git a/usr/src/cmd/sgs/yacc/Makefile.targ b/usr/src/cmd/sgs/yacc/Makefile.targ index 87f8c5221e..e97d36e4ff 100644 --- a/usr/src/cmd/sgs/yacc/Makefile.targ +++ b/usr/src/cmd/sgs/yacc/Makefile.targ @@ -100,4 +100,4 @@ $(LINTPOUT): $(SRCS) $(LINTLIB): $(LINTSRCS) - $(LINT.c) -o $(LIBNAME) $(LINTSRCS) + $(LINT.c) -o $(LIBNAME) $(LINTSRCS) > $(LINTOUT) 2>&1 diff --git a/usr/src/cmd/sgs/yacc/common/dextern.h b/usr/src/cmd/sgs/yacc/common/dextern.h index e90aa60468..54b441cac4 100644 --- a/usr/src/cmd/sgs/yacc/common/dextern.h +++ b/usr/src/cmd/sgs/yacc/common/dextern.h @@ -26,11 +26,13 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + #ifndef _DEXTERN_H #define _DEXTERN_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <inttypes.h> #include <ctype.h> @@ -42,6 +44,7 @@ #include <unistd.h> #include <stdlib.h> #include <wctype.h> +#include <limits.h> #ifdef __cplusplus extern "C" { @@ -301,6 +304,12 @@ extern int wscmp(const wchar_t *, const wchar_t *); extern char *parser; +#ifndef PBUFSIZE +#define PBUFSIZE PATH_MAX +#endif + +extern char pbuf[PBUFSIZE]; + /* default settings for a number of macros */ /* name of yacc tempfiles */ @@ -324,7 +333,11 @@ extern char *parser; #endif #ifndef PARSER -#define PARSER "/usr/share/lib/ccs/yaccpar" +#define PARSER "/share/lib/ccs/yaccpar" +#endif + +#ifndef PARSERPREFIX +#define PARSERPREFIX "/usr" #endif /* diff --git a/usr/src/cmd/sgs/yacc/common/y1.c b/usr/src/cmd/sgs/yacc/common/y1.c index 845f82d367..0e67d9047b 100644 --- a/usr/src/cmd/sgs/yacc/common/y1.c +++ b/usr/src/cmd/sgs/yacc/common/y1.c @@ -26,7 +26,9 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include "dextern.h" #include <sys/param.h> @@ -34,6 +36,7 @@ #include <unistd.h> #include <locale.h> #include <stdarg.h> /* For error() */ +#include <libgen.h> static void mktbls(void); static void others(void); @@ -236,6 +239,25 @@ mktbls() lsetsize = INIT_LSIZE + 1; } +static int +yacc_assemble_path(char *buf, size_t size, const char *file, int type) +{ + int ret; + char origin[PATH_MAX]; + + if (type != 0) { + ret = readlink("/proc/self/path/a.out", origin, PATH_MAX - 1); + if (ret < 0) + error(gettext( + "yacc: failed to read origin from /proc\n")); + origin[ret] = '\0'; + return (snprintf(buf, size, "%s/../%s", dirname(origin), + file)); + } + + return (snprintf(buf, size, "%s/%s", PARSERPREFIX, file)); +} + /* put out other arrays, copy the parsers */ static void others() @@ -244,7 +266,17 @@ others() int c, i, j; int tmpline; - finput = fopen(parser, "r"); + if (parser == NULL) { + parser = pbuf; + (void) yacc_assemble_path(pbuf, PBUFSIZE, PARSER, 1); + finput = fopen(parser, "r"); + if (finput == NULL) { + (void) yacc_assemble_path(pbuf, PBUFSIZE, PARSER, 0); + finput = fopen(parser, "r"); + } + } else { + finput = fopen(parser, "r"); + } if (finput == NULL) /* * TRANSLATION_NOTE -- This is a message from yacc. diff --git a/usr/src/cmd/sgs/yacc/common/y2.c b/usr/src/cmd/sgs/yacc/common/y2.c index 3599d40904..ca8dcc61f5 100644 --- a/usr/src/cmd/sgs/yacc/common/y2.c +++ b/usr/src/cmd/sgs/yacc/common/y2.c @@ -26,7 +26,9 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include "dextern.h" #include "sgs.h" @@ -60,7 +62,8 @@ char *infile; /* input file name */ static int numbval; /* value of an input number */ static int toksize = NAMESIZE; static wchar_t *tokname; /* input token name */ -char *parser = PARSER; /* location of common parser */ +char *parser = NULL; /* location of common parser */ +char pbuf[PBUFSIZE]; static void finact(void); static wchar_t *cstash(wchar_t *); diff --git a/usr/src/cmd/ssh/Makefile b/usr/src/cmd/ssh/Makefile index a36a9fb762..62f30b3f36 100644 --- a/usr/src/cmd/ssh/Makefile +++ b/usr/src/cmd/ssh/Makefile @@ -77,7 +77,7 @@ check: $(CHECKHDRS) _msg: $(RM) $(POFILE) $(TOUCH) $(POFILE) - $(MAKE) $(POFILE) XGETTEXT=/usr/bin/gxgettext + $(MAKE) $(POFILE) XGETTEXT=$(GNUXGETTEXT) $(CP) $(POFILE) $(MSGFILE) $(CP) $(MSGFILE) $(MSGDOMAIN) diff --git a/usr/src/cmd/ssh/include/key.h b/usr/src/cmd/ssh/include/key.h index 862b2d81d4..ec4993a9c1 100644 --- a/usr/src/cmd/ssh/include/key.h +++ b/usr/src/cmd/ssh/include/key.h @@ -39,11 +39,17 @@ extern "C" { typedef struct Key Key; enum types { - KEY_RSA1, - KEY_RSA, - KEY_DSA, + KEY_RSA1, + KEY_RSA, + KEY_DSA, + KEY_ECDSA, + KEY_RSA_CERT, + KEY_DSA_CERT, + KEY_ECDSA_CERT, + KEY_RSA_CERT_V00, + KEY_DSA_CERT_V00, KEY_NULL, - KEY_UNSPEC + KEY_UNSPEC }; enum fp_type { SSH_FP_SHA1, @@ -87,6 +93,7 @@ int key_names_valid2(const char *); int key_sign(Key *, u_char **, u_int *, u_char *, u_int); int key_verify(Key *, u_char *, u_int, u_char *, u_int); +int key_type_plain(int type); #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/ssh/include/servconf.h b/usr/src/cmd/ssh/include/servconf.h index a66c6415cb..9a32544c2a 100644 --- a/usr/src/cmd/ssh/include/servconf.h +++ b/usr/src/cmd/ssh/include/servconf.h @@ -167,6 +167,7 @@ typedef struct { char *pre_userauth_hook; char *pam_service_prefix; char *pam_service_name; + char *pubkey_plugin; } ServerOptions; diff --git a/usr/src/cmd/ssh/libssh/common/canohost.c b/usr/src/cmd/ssh/libssh/common/canohost.c index 2d427b9e8d..87aab396cf 100644 --- a/usr/src/cmd/ssh/libssh/common/canohost.c +++ b/usr/src/cmd/ssh/libssh/common/canohost.c @@ -73,9 +73,6 @@ get_remote_hostname(int socket, int verify_reverse_mapping) if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), NULL, 0, NI_NAMEREQD) != 0) { /* Host name not found. Use ip address. */ -#if 0 - log("Could not reverse map address %.100s.", ntop); -#endif return xstrdup(ntop); } @@ -206,36 +203,6 @@ get_socket_address(int socket, int remote, int flags) return (xstrdup(result)); } -#if 0 -static char * -get_socket_address(int socket, int remote, int flags) -{ - struct sockaddr_storage addr; - socklen_t addrlen; - char ntop[NI_MAXHOST]; - - /* Get IP address of client. */ - addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - - if (remote) { - if (getpeername(socket, (struct sockaddr *)&addr, &addrlen) - < 0) - return NULL; - } else { - if (getsockname(socket, (struct sockaddr *)&addr, &addrlen) - < 0) - return NULL; - } - /* Get the address in ascii. */ - if (getnameinfo((struct sockaddr *)&addr, addrlen, ntop, sizeof(ntop), - NULL, 0, flags) != 0) { - error("get_socket_ipaddr: getnameinfo %d failed", flags); - return NULL; - } - return xstrdup(ntop); -} -#endif char * get_peer_ipaddr(int socket) @@ -388,4 +355,4 @@ inet_ntop_native(int af, const void *src, char *dst, size_t size) } return (result); -} +} diff --git a/usr/src/cmd/ssh/libssh/common/key.c b/usr/src/cmd/ssh/libssh/common/key.c index f648d3b640..8ee2583d93 100644 --- a/usr/src/cmd/ssh/libssh/common/key.c +++ b/usr/src/cmd/ssh/libssh/common/key.c @@ -874,3 +874,20 @@ key_demote(Key *k) return (pk); } + +int +key_type_plain(int type) +{ + switch (type) { + case KEY_RSA_CERT_V00: + case KEY_RSA_CERT: + return KEY_RSA; + case KEY_DSA_CERT_V00: + case KEY_DSA_CERT: + return KEY_DSA; + case KEY_ECDSA_CERT: + return KEY_ECDSA; + default: + return type; + } +} diff --git a/usr/src/cmd/ssh/sftp-server/Makefile b/usr/src/cmd/ssh/sftp-server/Makefile index c2bdf26c1e..f49dde54ee 100644 --- a/usr/src/cmd/ssh/sftp-server/Makefile +++ b/usr/src/cmd/ssh/sftp-server/Makefile @@ -31,7 +31,7 @@ SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include ../Makefile.ssh-common -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lcrypto +LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lsunw_crypto POFILE_DIR = .. diff --git a/usr/src/cmd/ssh/sftp/Makefile b/usr/src/cmd/ssh/sftp/Makefile index 8bd8bb4ac3..f3c0ea7a75 100644 --- a/usr/src/cmd/ssh/sftp/Makefile +++ b/usr/src/cmd/ssh/sftp/Makefile @@ -35,7 +35,7 @@ SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include ../Makefile.ssh-common -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lcrypto -ltecla +LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lsunw_crypto -ltecla POFILE_DIR = .. diff --git a/usr/src/cmd/ssh/ssh-add/Makefile b/usr/src/cmd/ssh/ssh-add/Makefile index 1fb132f741..3235839e06 100644 --- a/usr/src/cmd/ssh/ssh-add/Makefile +++ b/usr/src/cmd/ssh/ssh-add/Makefile @@ -32,7 +32,7 @@ SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include ../Makefile.ssh-common -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lcrypto +LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lsunw_crypto POFILE_DIR= .. diff --git a/usr/src/cmd/ssh/ssh-agent/Makefile b/usr/src/cmd/ssh/ssh-agent/Makefile index 3d4a366c17..ab2e1eb49d 100644 --- a/usr/src/cmd/ssh/ssh-agent/Makefile +++ b/usr/src/cmd/ssh/ssh-agent/Makefile @@ -32,7 +32,7 @@ SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include ../Makefile.ssh-common -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lcrypto +LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lsunw_crypto POFILE_DIR= .. diff --git a/usr/src/cmd/ssh/ssh-keygen/Makefile b/usr/src/cmd/ssh/ssh-keygen/Makefile index f92c437045..0c90716768 100644 --- a/usr/src/cmd/ssh/ssh-keygen/Makefile +++ b/usr/src/cmd/ssh/ssh-keygen/Makefile @@ -32,7 +32,7 @@ SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include ../Makefile.ssh-common -LDLIBS += $(SSH_COMMON_LDLIBS) -lcrypto -lsocket +LDLIBS += $(SSH_COMMON_LDLIBS) -lsunw_crypto -lsocket POFILE_DIR= .. diff --git a/usr/src/cmd/ssh/ssh-keygen/ssh-keygen.c b/usr/src/cmd/ssh/ssh-keygen/ssh-keygen.c index ebad79b0f8..c79e76ae36 100644 --- a/usr/src/cmd/ssh/ssh-keygen/ssh-keygen.c +++ b/usr/src/cmd/ssh/ssh-keygen/ssh-keygen.c @@ -11,7 +11,7 @@ * called by a name other than "ssh" or "Secure Shell". */ -/* $OpenBSD: ssh-keygen.c,v 1.160 2007/01/21 01:41:54 stevesk Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.205 2011/01/11 06:13:10 djm Exp $ */ #include "includes.h" #include <openssl/evp.h> @@ -76,9 +76,14 @@ char *identity_new_passphrase = NULL; /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; -/* Dump public key file in format used by real and the original SSH 2 */ -int convert_to_ssh2 = 0; -int convert_from_ssh2 = 0; +/* Conversion to/from various formats */ +int convert_to = 0; +int convert_from = 0; +enum { + FMT_RFC4716, + FMT_PKCS8, + FMT_PEM +} convert_format = FMT_RFC4716; int print_public = 0; char *key_type_name = NULL; @@ -154,41 +159,105 @@ load_identity(char *filename) #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb static void -do_convert_to_ssh2(struct passwd *pw) +do_convert_to_ssh2(struct passwd *pw, Key *k) { - Key *k; u_int len; u_char *blob; - struct stat st; + char comment[61]; - if (!have_identity) - ask_filename(pw, gettext("Enter file in which the key is")); - if (stat(identity_file, &st) < 0) { - perror(identity_file); + if (key_to_blob(k, &blob, &len) <= 0) { + fprintf(stderr, "key_to_blob failed\n"); exit(1); } + /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ + snprintf(comment, sizeof(comment), + "%u-bit %s, converted by %s@%s from OpenSSH", + key_size(k), key_type(k), + pw->pw_name, hostname); + + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); + fprintf(stdout, "Comment: \"%s\"\n", comment); + dump_base64(stdout, blob, len); + fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); + key_free(k); + xfree(blob); + exit(0); +} + +static void +do_convert_to_pkcs8(Key *k) +{ + switch (key_type_plain(k->type)) { + case KEY_RSA: + if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) + fatal("PEM_write_RSA_PUBKEY failed"); + break; + case KEY_DSA: + if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) + fatal("PEM_write_DSA_PUBKEY failed"); + break; + default: + fatal("%s: unsupported key type %s", __func__, key_type(k)); + } + exit(0); +} + +static void +do_convert_to_pem(Key *k) +{ + switch (key_type_plain(k->type)) { + case KEY_RSA: + if (!PEM_write_RSAPublicKey(stdout, k->rsa)) + fatal("PEM_write_RSAPublicKey failed"); + break; + case KEY_DSA: + if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) + fatal("PEM_write_DSAPublicKey failed"); + break; + default: + fatal("%s: unsupported key type %s", __func__, key_type(k)); + } + exit(0); +} + +static void +do_convert_to(struct passwd *pw) +{ + Key *k; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((k = key_load_public(identity_file, NULL)) == NULL) { if ((k = load_identity(identity_file)) == NULL) { - fprintf(stderr, gettext("load failed\n")); + fprintf(stderr, "load failed\n"); exit(1); } } - if (key_to_blob(k, &blob, &len) <= 0) { - fprintf(stderr, gettext("key_to_blob failed\n")); + if (k->type == KEY_RSA1) { + fprintf(stderr, "version 1 keys are not supported\n"); exit(1); } - fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); - fprintf(stdout, gettext( - "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"), - key_size(k), key_type(k), - pw->pw_name, hostname); - dump_base64(stdout, blob, len); - fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); - key_free(k); - xfree(blob); + + switch (convert_format) { + case FMT_RFC4716: + do_convert_to_ssh2(pw, k); + break; + case FMT_PKCS8: + do_convert_to_pkcs8(k); + break; + case FMT_PEM: + do_convert_to_pem(k); + break; + default: + fatal("%s: unknown key format %d", __func__, convert_format); + } exit(0); } + static void buffer_get_bignum_bits(Buffer *b, BIGNUM *value) { @@ -327,24 +396,16 @@ get_line(FILE *fp, char *line, size_t len) } static void -do_convert_from_ssh2(struct passwd *pw) +do_convert_from_ssh2(struct passwd *pw, Key **k, int *private) { - Key *k; int blen; u_int len; char line[1024]; u_char blob[8096]; char encoded[8096]; - struct stat st; - int escaped = 0, private = 0, ok; + int escaped = 0; FILE *fp; - if (!have_identity) - ask_filename(pw, gettext("Enter file in which the key is")); - if (stat(identity_file, &st) < 0) { - perror(identity_file); - exit(1); - } fp = fopen(identity_file, "r"); if (fp == NULL) { perror(identity_file); @@ -357,7 +418,7 @@ do_convert_from_ssh2(struct passwd *pw) if (strncmp(line, "----", 4) == 0 || strstr(line, ": ") != NULL) { if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) - private = 1; + *private = 1; if (strstr(line, " END ") != NULL) { break; } @@ -382,26 +443,117 @@ do_convert_from_ssh2(struct passwd *pw) fprintf(stderr, gettext("uudecode failed.\n")); exit(1); } - k = private ? + *k = *private ? do_convert_private_ssh2_from_blob(blob, blen) : key_from_blob(blob, blen); - if (k == NULL) { + if (*k == NULL) { fprintf(stderr, gettext("decode blob failed.\n")); exit(1); } - ok = private ? - (k->type == KEY_DSA ? - PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : - PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : - key_write(k, stdout); + fclose(fp); +} + +static void +do_convert_from_pkcs8(Key **k, int *private) +{ + EVP_PKEY *pubkey; + FILE *fp; + + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { + fatal("%s: %s is not a recognised public key format", __func__, + identity_file); + } + fclose(fp); + switch (EVP_PKEY_type(pubkey->type)) { + case EVP_PKEY_RSA: + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_RSA; + (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); + break; + case EVP_PKEY_DSA: + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_DSA; + (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); + break; + default: + fatal("%s: unsupported pubkey type %d", __func__, + EVP_PKEY_type(pubkey->type)); + } + EVP_PKEY_free(pubkey); + return; +} + +static void +do_convert_from_pem(Key **k, int *private) +{ + FILE *fp; + RSA *rsa; + + if ((fp = fopen(identity_file, "r")) == NULL) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { + *k = key_new(KEY_UNSPEC); + (*k)->type = KEY_RSA; + (*k)->rsa = rsa; + fclose(fp); + return; + } + fatal("%s: unrecognised raw private key format", __func__); +} + +static void +do_convert_from(struct passwd *pw) +{ + Key *k = NULL; + int private = 0, ok = 0; + struct stat st; + + if (!have_identity) + ask_filename(pw, "Enter file in which the key is"); + if (stat(identity_file, &st) < 0) + fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); + + switch (convert_format) { + case FMT_RFC4716: + do_convert_from_ssh2(pw, &k, &private); + break; + case FMT_PKCS8: + do_convert_from_pkcs8(&k, &private); + break; + case FMT_PEM: + do_convert_from_pem(&k, &private); + break; + default: + fatal("%s: unknown key format %d", __func__, convert_format); + } + + if (!private) + ok = key_write(k, stdout); + if (ok) + fprintf(stdout, "\n"); + else { + switch (k->type) { + case KEY_DSA: + ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, + NULL, 0, NULL, NULL); + break; + case KEY_RSA: + ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, + NULL, 0, NULL, NULL); + break; + default: + fatal("%s: unsupported key type %s", __func__, + key_type(k)); + } + } + if (!ok) { - fprintf(stderr, gettext("key write failed")); + fprintf(stderr, "key write failed\n"); exit(1); } key_free(k); - if (!private) - fprintf(stdout, "\n"); - fclose(fp); exit(0); } @@ -917,12 +1069,13 @@ usage(void) " -B Show bubblebabble digest of key file.\n" " -c Change comment in private and public key files.\n" " -C comment Provide new comment.\n" - " -e Convert OpenSSH to IETF SECSH key file.\n" + " -e Convert OpenSSH to foreign format key file.\n" " -f filename Filename of the key file.\n" " -F hostname Find hostname in known hosts file.\n" " -H Hash names in known_hosts file.\n" - " -i Convert IETF SECSH to OpenSSH key file.\n" + " -i Convert foreign format to OpenSSH key file.\n" " -l Show fingerprint of key file.\n" + " -m key_fmt Conversion format for -e/-i (PEM|PKCS8|RFC4716).\n" " -N phrase Provide new passphrase.\n" " -p Change passphrase of private key file.\n" " -P phrase Provide old passphrase.\n" @@ -974,7 +1127,7 @@ main(int argc, char **argv) exit(1); } -#define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:" +#define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:m:N:P:R:t:" while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) { switch (opt) { @@ -1002,6 +1155,22 @@ main(int argc, char **argv) case 'B': print_bubblebabble = 1; break; + case 'm': + if (strcasecmp(optarg, "RFC4716") == 0 || + strcasecmp(optarg, "ssh2") == 0) { + convert_format = FMT_RFC4716; + break; + } + if (strcasecmp(optarg, "PKCS8") == 0) { + convert_format = FMT_PKCS8; + break; + } + if (strcasecmp(optarg, "PEM") == 0) { + convert_format = FMT_PEM; + break; + } + fatal("Unsupported conversion format \"%s\"", optarg); + /*NOTREACHED*/ case 'p': change_passphrase = 1; break; @@ -1027,12 +1196,12 @@ main(int argc, char **argv) case 'e': case 'x': /* export key */ - convert_to_ssh2 = 1; + convert_to = 1; break; case 'i': case 'X': /* import key */ - convert_from_ssh2 = 1; + convert_from = 1; break; case 'y': print_public = 1; @@ -1064,10 +1233,10 @@ main(int argc, char **argv) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); - if (convert_to_ssh2) - do_convert_to_ssh2(pw); - if (convert_from_ssh2) - do_convert_from_ssh2(pw); + if (convert_to) + do_convert_to(pw); + if (convert_from) + do_convert_from(pw); if (print_public) do_print_public(pw); diff --git a/usr/src/cmd/ssh/ssh-keyscan/Makefile b/usr/src/cmd/ssh/ssh-keyscan/Makefile index 9e2fd17160..90428880a3 100644 --- a/usr/src/cmd/ssh/ssh-keyscan/Makefile +++ b/usr/src/cmd/ssh/ssh-keyscan/Makefile @@ -32,7 +32,7 @@ SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include ../Makefile.ssh-common -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lnsl -lz -lcrypto +LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lnsl -lz -lsunw_crypto POFILE_DIR= .. diff --git a/usr/src/cmd/ssh/ssh-keysign/Makefile b/usr/src/cmd/ssh/ssh-keysign/Makefile index e31ae681a1..649935a050 100644 --- a/usr/src/cmd/ssh/ssh-keysign/Makefile +++ b/usr/src/cmd/ssh/ssh-keysign/Makefile @@ -36,7 +36,7 @@ include ../Makefile.ssh-common FILEMODE= 04555 -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lnsl -lz -lcrypto +LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket -lnsl -lz -lsunw_crypto POFILE_DIR= .. diff --git a/usr/src/cmd/ssh/ssh/Makefile b/usr/src/cmd/ssh/ssh/Makefile index 7bdd4f6be5..2d77334497 100644 --- a/usr/src/cmd/ssh/ssh/Makefile +++ b/usr/src/cmd/ssh/ssh/Makefile @@ -40,7 +40,7 @@ include ../Makefile.ssh-common LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket \ -lnsl \ -lz \ - -lcrypto \ + -lsunw_crypto \ -lgss POFILE_DIR= .. diff --git a/usr/src/cmd/ssh/sshd/Makefile b/usr/src/cmd/ssh/sshd/Makefile index a52e9b1cc8..4c82633347 100644 --- a/usr/src/cmd/ssh/sshd/Makefile +++ b/usr/src/cmd/ssh/sshd/Makefile @@ -75,7 +75,7 @@ LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket \ -lpam \ -lbsm \ -lwrap \ - -lcrypto \ + -lsunw_crypto \ -lgss \ -lcontract MAPFILES = $(MAPFILE.INT) $(MAPFILE.NGB) diff --git a/usr/src/cmd/ssh/sshd/auth2-pubkey.c b/usr/src/cmd/ssh/sshd/auth2-pubkey.c index 658634c195..c1c5f540e4 100644 --- a/usr/src/cmd/ssh/sshd/auth2-pubkey.c +++ b/usr/src/cmd/ssh/sshd/auth2-pubkey.c @@ -26,6 +26,8 @@ * Use is subject to license terms. */ +#include <dlfcn.h> + #include "includes.h" RCSID("$OpenBSD: auth2-pubkey.c,v 1.2 2002/05/31 11:35:15 markus Exp $"); @@ -54,6 +56,13 @@ extern ServerOptions options; extern u_char *session_id2; extern int session_id2_len; +/* global plugin function requirements */ +static const char *RSA_SYM_NAME = "sshd_user_rsa_key_allowed"; +static const char *DSA_SYM_NAME = "sshd_user_rsa_key_allowed"; +typedef int (*RSA_SYM)(struct passwd *, RSA *, const char *); +typedef int (*DSA_SYM)(struct passwd *, DSA *, const char *); + + static void userauth_pubkey(Authctxt *authctxt) { @@ -309,7 +318,98 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) return found_key; } -/* check whether given key is in .ssh/authorized_keys* */ +/** + * Checks whether or not access is allowed based on a plugin specified + * in sshd_config (PubKeyPlugin). + * + * Note that this expects a symbol in the loaded library that takes + * the current user (pwd entry), the current RSA key and it's fingerprint. + * The symbol is expected to return 1 on success and 0 on failure. + * + * While we could optimize this code to dlopen once in the process' lifetime, + * sshd is already a slow beast, so this is really not a concern. + * The overhead is basically a rounding error compared to everything else, and + * it keeps this code minimally invasive. + */ +static int +user_key_allowed_from_plugin(struct passwd *pw, Key *key) +{ + RSA_SYM rsa_sym = NULL; + DSA_SYM dsa_sym = NULL; + char *fp = NULL; + void *handle = NULL; + int success = 0; + + if (options.pubkey_plugin == NULL || pw == NULL || key == NULL || + (key->type != KEY_RSA && key->type != KEY_RSA1 && + key->type != KEY_DSA && key->type != KEY_ECDSA)) + return success; + + handle = dlopen(options.pubkey_plugin, RTLD_NOW); + if ((handle == NULL)) { + debug("Unable to open library %s: %s", options.pubkey_plugin, + dlerror()); + goto out; + } + + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); + if (fp == NULL) { + debug("failed to generate fingerprint"); + goto out; + } + + switch (key->type) { + case KEY_RSA1: + case KEY_RSA: + rsa_sym = (RSA_SYM)dlsym(handle, RSA_SYM_NAME); + if (rsa_sym == NULL) { + debug("Unable to resolve symbol %s: %s", RSA_SYM_NAME, + dlerror()); + goto out; + } + debug2("Invoking %s from %s", RSA_SYM_NAME, + options.pubkey_plugin); + success = (*rsa_sym)(pw, key->rsa, fp); + break; + case KEY_DSA: + case KEY_ECDSA: + dsa_sym = (DSA_SYM)dlsym(handle, RSA_SYM_NAME); + if (dsa_sym == NULL) { + debug("Unable to resolve symbol %s: %s", DSA_SYM_NAME, + dlerror()); + goto out; + } + debug2("Invoking %s from %s", DSA_SYM_NAME, + options.pubkey_plugin); + success = (*dsa_sym)(pw, key->dsa, fp); + break; + default: + debug2("user_key_plugins only support RSA keys"); + } + + debug("sshd_plugin returned: %d", success); + +out: + if (handle != NULL) { + dlclose(handle); + dsa_sym = NULL; + rsa_sym = NULL; + handle = NULL; + } + + if (success) + verbose("Found matching %s key: %s", key_type(key), fp); + + if (fp != NULL) { + xfree(fp); + fp = NULL; + } + + return success; +} + + +/* check whether given key is in .ssh/authorized_keys or a plugin */ int user_key_allowed(struct passwd *pw, Key *key) { @@ -329,6 +429,13 @@ user_key_allowed(struct passwd *pw, Key *key) file = authorized_keys_file2(pw); success = user_key_allowed2(pw, key, file); xfree(file); + + if (success) + return success; + + /* try from a plugin */ + success = user_key_allowed_from_plugin(pw, key); + return success; } diff --git a/usr/src/cmd/ssh/sshd/servconf.c b/usr/src/cmd/ssh/sshd/servconf.c index 516466bbc1..16f1dcecf7 100644 --- a/usr/src/cmd/ssh/sshd/servconf.c +++ b/usr/src/cmd/ssh/sshd/servconf.c @@ -155,6 +155,7 @@ initialize_server_options(ServerOptions *options) options->pre_userauth_hook = NULL; options->pam_service_name = NULL; options->pam_service_prefix = NULL; + options->pubkey_plugin = NULL; } #ifdef HAVE_DEFOPEN @@ -419,13 +420,14 @@ typedef enum { sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, - sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, + sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sBanner, sVerifyReverseMapping, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, sMaxAuthTries, sMaxAuthTriesLog, sUsePrivilegeSeparation, sLookupClientHostnames, sUseOpenSSLEngine, sChrootDirectory, sPreUserauthHook, sMatch, sPAMServicePrefix, sPAMServiceName, + sMaxStartups, sPubKeyPlugin, sDeprecated } ServerOpCodes; @@ -532,6 +534,7 @@ static struct { { "match", sMatch, SSHCFG_ALL }, { "pamserviceprefix", sPAMServicePrefix, SSHCFG_GLOBAL }, { "pamservicename", sPAMServiceName, SSHCFG_GLOBAL }, + { "pubkeyplugin", sPubKeyPlugin, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; @@ -1356,6 +1359,10 @@ parse_flag: options->pam_service_name = xstrdup(arg); break; + case sPubKeyPlugin: + charptr = &options->pubkey_plugin; + goto parse_filename; + default: fatal("%s line %d: Missing handler for opcode %s (%d)", filename, linenum, arg, opcode); diff --git a/usr/src/cmd/ssh/sshd/sshlogin.c b/usr/src/cmd/ssh/sshd/sshlogin.c index c21877355c..c2bd3bacb7 100644 --- a/usr/src/cmd/ssh/sshd/sshlogin.c +++ b/usr/src/cmd/ssh/sshd/sshlogin.c @@ -101,8 +101,7 @@ record_login(pid_t pid, const char *ttyname, const char *progname, fatal_cleanup(); } } - remote_name_or_ip = get_remote_name_or_ip(utmp_len, - options.verify_reverse_mapping); + remote_name_or_ip = get_remote_ipaddr(); initialized = 1; } diff --git a/usr/src/cmd/stat/Makefile b/usr/src/cmd/stat/Makefile index 34149b2b37..faffc6a437 100644 --- a/usr/src/cmd/stat/Makefile +++ b/usr/src/cmd/stat/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2011, 2012, Joyent, Inc. All rights reserved. # Use is subject to license terms. # # cmd/stat/Makefile @@ -27,7 +27,14 @@ include ../Makefile.cmd -SUBDIRS= arcstat iostat mpstat vmstat fsstat kstat +SUBDIRS= arcstat \ + fsstat \ + iostat \ + kstat \ + mpstat \ + vfsstat \ + vmstat \ + ziostat all := TARGET = all install := TARGET = install diff --git a/usr/src/cmd/stat/arcstat/Makefile b/usr/src/cmd/stat/arcstat/Makefile index 6ae60a8d3d..a98e2fee7e 100644 --- a/usr/src/cmd/stat/arcstat/Makefile +++ b/usr/src/cmd/stat/arcstat/Makefile @@ -11,6 +11,7 @@ # # Copyright 2014 Adam Stevko. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # include $(SRC)/cmd/Makefile.cmd diff --git a/usr/src/cmd/stat/arcstat/arcstat.pl b/usr/src/cmd/stat/arcstat/arcstat.pl index 8f13221910..8f13221910 100755..100644 --- a/usr/src/cmd/stat/arcstat/arcstat.pl +++ b/usr/src/cmd/stat/arcstat/arcstat.pl diff --git a/usr/src/cmd/stat/vfsstat/Makefile b/usr/src/cmd/stat/vfsstat/Makefile new file mode 100644 index 0000000000..04b5085243 --- /dev/null +++ b/usr/src/cmd/stat/vfsstat/Makefile @@ -0,0 +1,41 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +include $(SRC)/cmd/Makefile.cmd + +PROG= vfsstat + +.KEEP_STATE: + +all: $(PROG) + +install: all .WAIT $(ROOTPROG) + +clean: + +$(ROOTBINPROG): $(PROG) + $(INS.file) + +lint: + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/stat/vfsstat/vfsstat.pl b/usr/src/cmd/stat/vfsstat/vfsstat.pl new file mode 100644 index 0000000000..a3780b8e63 --- /dev/null +++ b/usr/src/cmd/stat/vfsstat/vfsstat.pl @@ -0,0 +1,227 @@ +#!/usr/perl5/bin/perl -w +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011 Joyent, Inc. +# +# vfsstat - report VFS statistics per zone +# +# USAGE: vfsstat [-hIMrzZ] [interval [count]] +# -h # help +# -I # print results per interval (where applicable) +# -M # print results in MB/s +# -r # print data in comma-separated format +# -z # hide zones with no VFS activity +# -Z # print data for all zones +# +# eg, vfsstat # print summary since zone boot +# vfsstat 1 # print continually every 1 second +# vfsstat 1 5 # print 5 times, every 1 second +# +# NOTES: +# +# - The calculations and output fields emulate those from iostat(1M) as closely +# as possible. When only one zone is actively performing disk I/O, the +# results from iostat(1M) in the global zone and vfsstat in the local zone +# should be almost identical. Note that many VFS read operations are handled +# by the ARC, so vfsstat and iostat(1M) will be similar only when most +# requests are missing in the ARC. +# +# - As with iostat(1M), a result of 100% for VFS read and write utilization does +# not mean that the syscall layer is fully saturated. Instead, that +# measurement just shows that at least one operation was pending over the last +# quanta of time examined. Since the VFS layer can process more than one +# operation concurrently, this measurement will frequently be 100% but the VFS +# layer can still accept additional requests. +# +# - This script is based on Brendan Gregg's K9Toolkit examples: +# +# http://www.brendangregg.com/k9toolkit.html +# + +use Getopt::Std; +use Sun::Solaris::Kstat; +my $Kstat = Sun::Solaris::Kstat->new(); + +# Process command line args +usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; +getopts('hIMrzZ') or usage(); +usage() if defined $main::opt_h; +$main::opt_h = 0; + +my $USE_MB = defined $main::opt_M ? $main::opt_M : 0; +my $USE_INTERVAL = defined $main::opt_I ? $main::opt_I : 0; +my $USE_COMMA = defined $main::opt_r ? $main::opt_r : 0; +my $HIDE_ZEROES = defined $main::opt_z ? $main::opt_z : 0; +my $ALL_ZONES = defined $main::opt_Z ? $main::opt_Z : 0; + +my ($interval, $count); +if ( defined($ARGV[0]) ) { + $interval = $ARGV[0]; + $count = defined ($ARGV[1]) ? $ARGV[1] : 2**32; + usage() if ($interval == 0); +} else { + $interval = 1; + $count = 1; +} + +my $HEADER_FMT = $USE_COMMA ? + "r/%s,w/%s,%sr/%s,%sw/%s,ractv,wactv,read_t,writ_t,%%r,%%w," . + "d/%s,del_t,zone\n" : + " r/%s w/%s %sr/%s %sw/%s ractv wactv read_t writ_t " . + "%%r %%w d/%s del_t zone\n"; +my $DATA_FMT = $USE_COMMA ? + "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%d,%d,%.1f,%.1f,%s,%d\n" : + "%5.1f %5.1f %5.1f %5.1f %5.1f %5.1f %6.1f %6.1f %3d %3d " . + "%5.1f %6.1f %s (%d)\n"; + +my $BYTES_PREFIX = $USE_MB ? "M" : "k"; +my $BYTES_DIVISOR = $USE_MB ? 1024 * 1024 : 1024; +my $INTERVAL_SUFFIX = $USE_INTERVAL ? "i" : "s"; +my $NANOSEC = 1000000000; + +my @fields = ( 'reads', 'writes', 'nread', 'nwritten', 'rtime', 'wtime', + 'rlentime', 'wlentime', 'delay_cnt', 'delay_time', 'snaptime' ); + +chomp(my $curzone = (`/sbin/zonename`)); + +my %old = (); +my $rows_printed = 0; + +for (my $ii = 0; $ii < $count; $ii++) { + # Read list of visible zones and their zone IDs + my @zones = (); + my %zoneids = (); + my $zoneadm = `zoneadm list -p | cut -d: -f1,2`; + @lines = split(/\n/, $zoneadm); + foreach $line (@lines) { + @tok = split(/:/, $line); + $zoneids->{$tok[1]} = $tok[0]; + push(@zones, $tok[1]); + } + + $Kstat->update(); + + # Print the column header every 20 rows + if ($rows_printed == 0 || $ALL_ZONES) { + printf($HEADER_FMT, $INTERVAL_SUFFIX, $INTERVAL_SUFFIX, + $BYTES_PREFIX, $INTERVAL_SUFFIX, $BYTES_PREFIX, + $INTERVAL_SUFFIX, $INTERVAL_SUFFIX); + } + + $rows_printed = $rows_printed >= 20 ? 0 : $rows_printed + 1; + + foreach $zone (@zones) { + if ((!$ALL_ZONES) && ($zone ne $curzone)) { + next; + } + + if (! defined $old->{$zone}) { + $old->{$zone} = (); + foreach $field (@fields) { $old->{$zone}->{$field} = 0; } + } + + # + # Kstats have a 30-character limit (KSTAT_STRLEN) on their + # names, so if the zone name exceeds that limit, use the first + # 30 characters. + # + my $trimmed_zone = substr($zone, 0, 30); + my $zoneid = $zoneids->{$zone}; + + print_stats($zone, $zoneid, + $Kstat->{'zone_vfs'}{$zoneid}{$trimmed_zone}, $old->{$zone}); + } + + sleep ($interval); +} + +exit(0); + +sub print_stats { + my $zone = $_[0]; + my $zoneid = $_[1]; + my $data = $_[2]; + my $old = $_[3]; + + my $etime = $data->{'snaptime'} - + ($old->{'snaptime'} > 0 ? $old->{'snaptime'} : $data->{'crtime'}); + + # Calculate basic statistics + my $rate_divisor = $USE_INTERVAL ? 1 : $etime; + my $reads = ($data->{'reads'} - $old->{'reads'}) / $rate_divisor; + my $writes = ($data->{'writes'} - $old->{'writes'}) / $rate_divisor; + my $nread = ($data->{'nread'} - $old->{'nread'}) / + $rate_divisor / $BYTES_DIVISOR; + my $nwritten = ($data->{'nwritten'} - $old->{'nwritten'}) / + $rate_divisor / $BYTES_DIVISOR; + + # Calculate transactions per second + my $r_tps = ($data->{'reads'} - $old->{'reads'}) / $etime; + my $w_tps = ($data->{'writes'} - $old->{'writes'}) / $etime; + + # Calculate average length of active queue + my $r_actv = (($data->{'rlentime'} - $old->{'rlentime'}) / $NANOSEC) / + $etime; + my $w_actv = (($data->{'wlentime'} - $old->{'wlentime'}) / $NANOSEC) / + $etime; + + # Calculate average service time + # multiply by 1000 to convert to usecs for conssistency with del_t + my $read_t = ($r_tps > 0 ? $r_actv * (1000 / $r_tps) : 0.0) * 1000; + my $writ_t = ($w_tps > 0 ? $w_actv * (1000 / $w_tps) : 0.0) * 1000; + + # Calculate I/O throttle delay metrics + my $delays = $data->{'delay_cnt'} - $old->{'delay_cnt'}; + my $d_tps = $delays / $etime; + my $del_t = $delays > 0 ? + ($data->{'delay_time'} - $old->{'delay_time'}) / $delays : 0.0; + + # Calculate the % time the VFS layer is active + my $r_b_pct = ((($data->{'rtime'} - $old->{'rtime'}) / $NANOSEC) / + $etime) * 100; + my $w_b_pct = ((($data->{'wtime'} - $old->{'wtime'}) / $NANOSEC) / + $etime) * 100; + + if (! $HIDE_ZEROES || $reads != 0.0 || $writes != 0.0 || + $nread != 0.0 || $nwritten != 0.0) { + printf($DATA_FMT, $reads, $writes, $nread, $nwritten, $r_actv, + $w_actv, $read_t, $writ_t, $r_b_pct, $w_b_pct, + $d_tps, $del_t, substr($zone, 0, 8), $zoneid); + } + + # Save current calculations for next loop + foreach (@fields) { $old->{$_} = $data->{$_}; } +} + +sub usage { + print STDERR <<END; +USAGE: vfsstat [-hIMrzZ] [interval [count]] + eg, vfsstat # print summary since zone boot + vfsstat 1 # print continually every 1 second + vfsstat 1 5 # print 5 times, every 1 second + vfsstat -I # print results per interval (where applicable) + vfsstat -M # print results in MB/s + vfsstat -r # print results in comma-separated format + vfsstat -z # hide zones with no VFS activity + vfsstat -Z # print results for all zones +END + exit 1; +} diff --git a/usr/src/cmd/stat/ziostat/Makefile b/usr/src/cmd/stat/ziostat/Makefile new file mode 100644 index 0000000000..c338b59678 --- /dev/null +++ b/usr/src/cmd/stat/ziostat/Makefile @@ -0,0 +1,41 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +include $(SRC)/cmd/Makefile.cmd + +PROG= ziostat + +.KEEP_STATE: + +all: $(PROG) + +install: all .WAIT $(ROOTPROG) + +clean: + +$(ROOTBINPROG): $(PROG) + $(INS.file) + +lint: + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/stat/ziostat/ziostat.pl b/usr/src/cmd/stat/ziostat/ziostat.pl new file mode 100755 index 0000000000..cf95d2f5a5 --- /dev/null +++ b/usr/src/cmd/stat/ziostat/ziostat.pl @@ -0,0 +1,204 @@ +#!/usr/perl5/bin/perl -w +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011 Joyent, Inc. +# +# ziostat - report I/O statistics per zone +# +# USAGE: ziostat [-hIMrzZ] [interval [count]] +# -h # help +# -I # print results per interval (where applicable) +# -M # print results in MB/s +# -r # print data in comma-separated format +# -z # hide zones with no ZFS I/O activity +# -Z # print data for all zones +# +# eg, ziostat # print summary since zone boot +# ziostat 1 # print continually every 1 second +# ziostat 1 5 # print 5 times, every 1 second +# +# NOTES: +# +# - The calculations and output fields emulate those from iostat(1M) as closely +# as possible. When only one zone is actively performing disk I/O, the +# results from iostat(1M) in the global zone and ziostat in the local zone +# should be almost identical. +# +# - As with iostat(1M), a result of 100% for disk utilization does not mean that +# the disk is fully saturated. Instead, that measurement just shows that at +# least one operation was pending over the last quanta of time examined. +# Since disk devices can process more than one operation concurrently, this +# measurement will frequently be 100% but the disk can still offer higher +# performance. +# +# - This script is based on Brendan Gregg's K9Toolkit examples: +# +# http://www.brendangregg.com/k9toolkit.html +# + +use Getopt::Std; +use Sun::Solaris::Kstat; +my $Kstat = Sun::Solaris::Kstat->new(); + +# Process command line args +usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; +getopts('hIMrzZ') or usage(); +usage() if defined $main::opt_h; +$main::opt_h = 0; + +my $USE_MB = defined $main::opt_M ? $main::opt_M : 0; +my $USE_INTERVAL = defined $main::opt_I ? $main::opt_I : 0; +my $USE_COMMA = defined $main::opt_r ? $main::opt_r : 0; +my $HIDE_ZEROES = defined $main::opt_z ? $main::opt_z : 0; +my $ALL_ZONES = defined $main::opt_Z ? $main::opt_Z : 0; + +my ($interval, $count); +if ( defined($ARGV[0]) ) { + $interval = $ARGV[0]; + $count = defined ($ARGV[1]) ? $ARGV[1] : 2**32; + usage() if ($interval == 0); +} else { + $interval = 1; + $count = 1; +} + +my $HEADER_FMT = $USE_COMMA ? + "r/%s,%sr/%s,actv,wsvc_t,asvc_t,%%b,zone\n" : + " r/%s %sr/%s actv wsvc_t asvc_t %%b zone\n"; +my $DATA_FMT = $USE_COMMA ? + "%.1f,%.1f,%.1f,%.1f,%.1f,%d,%s,%d\n" : + " %6.1f %6.1f %6.1f %6.1f %6.1f %3d %s (%d)\n"; + +my $BYTES_PREFIX = $USE_MB ? "M" : "k"; +my $BYTES_DIVISOR = $USE_MB ? 1024 * 1024 : 1024; +my $INTERVAL_SUFFIX = $USE_INTERVAL ? "i" : "s"; +my $NANOSEC = 1000000000; + +my @fields = ( 'reads', 'nread', 'waittime', 'rtime', 'rlentime', 'snaptime' ); + +chomp(my $curzone = (`/sbin/zonename`)); + +# Read list of visible zones and their zone IDs +my @zones = (); +my %zoneids = (); +my $zoneadm = `zoneadm list -p | cut -d: -f1,2`; +@lines = split(/\n/, $zoneadm); +foreach $line (@lines) { + @tok = split(/:/, $line); + $zoneids->{$tok[1]} = $tok[0]; + push(@zones, $tok[1]); +} + +my %old = (); +my $rows_printed = 0; + +$Kstat->update(); + +for (my $ii = 0; $ii < $count; $ii++) { + # Print the column header every 20 rows + if ($rows_printed == 0 || $ALL_ZONES) { + printf($HEADER_FMT, $INTERVAL_SUFFIX, $BYTES_PREFIX, + $INTERVAL_SUFFIX, $INTERVAL_SUFFIX); + } + + $rows_printed = $rows_printed >= 20 ? 0 : $rows_printed + 1; + + foreach $zone (@zones) { + if ((!$ALL_ZONES) && ($zone ne $curzone)) { + next; + } + + if (! defined $old->{$zone}) { + $old->{$zone} = (); + foreach $field (@fields) { $old->{$zone}->{$field} = 0; } + } + + # + # Kstats have a 30-character limit (KSTAT_STRLEN) on their + # names, so if the zone name exceeds that limit, use the first + # 30 characters. + # + my $trimmed_zone = substr($zone, 0, 30); + my $zoneid = $zoneids->{$zone}; + + print_stats($zone, $zoneid, + $Kstat->{'zone_zfs'}{$zoneid}{$trimmed_zone}, $old->{$zone}); + } + + sleep ($interval); + $Kstat->update(); +} + +sub print_stats { + my $zone = $_[0]; + my $zoneid = $_[1]; + my $data = $_[2]; + my $old = $_[3]; + + my $etime = $data->{'snaptime'} - + ($old->{'snaptime'} > 0 ? $old->{'snaptime'} : $data->{'crtime'}); + + # Calculate basic statistics + my $rate_divisor = $USE_INTERVAL ? 1 : $etime; + my $reads = ($data->{'reads'} - $old->{'reads'}) / $rate_divisor; + my $nread = ($data->{'nread'} - $old->{'nread'}) / + $rate_divisor / $BYTES_DIVISOR; + + # Calculate overall transactions per second + my $ops = $data->{'reads'} - $old->{'reads'}; + my $tps = $ops / $etime; + + # Calculate average length of disk run queue + my $actv = (($data->{'rlentime'} - $old->{'rlentime'}) / $NANOSEC) / + $etime; + + # Calculate average disk wait and service times + my $wsvc = $ops > 0 ? (($data->{'waittime'} - $old->{'waittime'}) / + 1000000) / $ops : 0.0; + my $asvc = $tps > 0 ? $actv * (1000 / $tps) : 0.0; + + # Calculate the % time the disk run queue is active + my $b_pct = ((($data->{'rtime'} - $old->{'rtime'}) / $NANOSEC) / + $etime) * 100; + + if (! $HIDE_ZEROES || $reads != 0.0 || $nread != 0.0 ) { + printf($DATA_FMT, $reads, $nread, $actv, $wsvc, $asvc, + $b_pct, substr($zone, 0, 8), $zoneid); + } + + # Save current calculations for next loop + foreach (@fields) { $old->{$_} = $data->{$_}; } +} + +sub usage { + print STDERR <<END; +USAGE: ziostat [-hIMrzZ] [interval [count]] + eg, ziostat # print summary since zone boot + ziostat 1 # print continually every 1 second + ziostat 1 5 # print 5 times, every 1 second + ziostat -I # print results per interval (where applicable) + ziostat -M # print results in MB/s + ziostat -r # print results in comma-separated format + ziostat -z # hide zones with no ZFS I/O activity + ziostat -Z # print results for all zones +END + exit 1; +} diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c index 3cc30e3e67..149f2a6cb5 100644 --- a/usr/src/cmd/svc/configd/rc_node.c +++ b/usr/src/cmd/svc/configd/rc_node.c @@ -23,6 +23,9 @@ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * rc_node.c - In-memory SCF object management diff --git a/usr/src/cmd/svc/milestone/net-routing-setup b/usr/src/cmd/svc/milestone/net-routing-setup index 6ab1a6c7f0..b4ee7d39ac 100644 --- a/usr/src/cmd/svc/milestone/net-routing-setup +++ b/usr/src/cmd/svc/milestone/net-routing-setup @@ -21,11 +21,15 @@ # # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. +# +# Copyright (c) 2012 Joyent, Inc. All rights reserved. # This script configures IP routing. . /lib/svc/share/smf_include.sh +set -o xtrace + # # In a shared-IP zone we need this service to be up, but all of the work # it tries to do is irrelevant (and will actually lead to the service @@ -156,7 +160,8 @@ fi # however, as persistent daemon state is now controlled by SMF. # ipv4_routing_set=`/usr/bin/svcprop -p routeadm/ipv4-routing-set $SMF_FMRI` -if [ -z "$defrouters" ]; then +smartos_param=`/usr/bin/bootparams | grep "^smartos"` +if [ -z "$defrouters" ] && [ "$smartos_param" != "" ]; then # # Set default value for ipv4-routing to enabled. If routeadm -e/-d # has not yet been run by the administrator, we apply this default. @@ -210,5 +215,21 @@ if [ -f /etc/inet/static_routes ]; then done fi +# +# Read /etc/inet/static_routes.vmadm and add each route. +# +if [ -f /etc/inet/static_routes.vmadm ]; then + echo "Adding vmadm persistent routes:" + /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm | while read line; do + /usr/sbin/route add $line + done +fi + +# +# Log the result +# +echo "Routing setup complete:" +/usr/bin/netstat -rn + # Clear exit status. exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/network-location.xml b/usr/src/cmd/svc/milestone/network-location.xml index aad337f42f..709e9df8f3 100644 --- a/usr/src/cmd/svc/milestone/network-location.xml +++ b/usr/src/cmd/svc/milestone/network-location.xml @@ -106,7 +106,7 @@ --> <dependency name='manifest-import' - grouping='require_all' + grouping='optional_all' restart_on='none' type='service'> <service_fmri value='svc:/system/manifest-import:default' /> diff --git a/usr/src/cmd/svc/milestone/network-routing-setup.xml b/usr/src/cmd/svc/milestone/network-routing-setup.xml index b34d578e2a..85a74756da 100644 --- a/usr/src/cmd/svc/milestone/network-routing-setup.xml +++ b/usr/src/cmd/svc/milestone/network-routing-setup.xml @@ -40,11 +40,19 @@ <!-- loopback/physical network configuration is required --> <dependency - name='network' - grouping='optional_all' + name='loopback' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + </dependency> + + <dependency + name='physical' + grouping='require_all' restart_on='none' type='service'> - <service_fmri value='svc:/milestone/network' /> + <service_fmri value='svc:/network/physical' /> </dependency> <!-- usr filesystem required to run routing-related commands --> diff --git a/usr/src/cmd/svc/milestone/network.xml b/usr/src/cmd/svc/milestone/network.xml index 75b5578f44..48386ebf73 100644 --- a/usr/src/cmd/svc/milestone/network.xml +++ b/usr/src/cmd/svc/milestone/network.xml @@ -54,6 +54,14 @@ <service_fmri value='svc:/network/physical' /> </dependency> + <dependency + name='routing-setup' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/routing-setup' /> + </dependency> + <exec_method type='method' name='start' diff --git a/usr/src/cmd/svc/milestone/single-user.xml b/usr/src/cmd/svc/milestone/single-user.xml index 8797f13c47..579ecb5ddd 100644 --- a/usr/src/cmd/svc/milestone/single-user.xml +++ b/usr/src/cmd/svc/milestone/single-user.xml @@ -68,7 +68,7 @@ <dependency name='manifests' - grouping='require_all' + grouping='optional_all' restart_on='none' type='service'> <service_fmri value='svc:/system/manifest-import' /> diff --git a/usr/src/cmd/svc/shell/smf_include.sh b/usr/src/cmd/svc/shell/smf_include.sh index d0dc387246..02c9532763 100644 --- a/usr/src/cmd/svc/shell/smf_include.sh +++ b/usr/src/cmd/svc/shell/smf_include.sh @@ -22,6 +22,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012 Joyent, Inc. All rights reserved. # smf_present () { @@ -234,7 +235,12 @@ smf_kill_contract() { # SMF_EXIT_ERR_OTHER, although not defined, encompasses all non-zero # exit status values. # +# The SMF_EXIT_NODAEMON exit status should be used when a method does not +# need to run any persistent process. This indicates success, abandons the +# contract, and allows dependencies to be met. +# SMF_EXIT_OK=0 +SMF_EXIT_NODAEMON=94 SMF_EXIT_ERR_FATAL=95 SMF_EXIT_ERR_CONFIG=96 SMF_EXIT_MON_DEGRADE=97 diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c index 7fbf17a6ec..c8e0872ff8 100644 --- a/usr/src/cmd/svc/startd/graph.c +++ b/usr/src/cmd/svc/startd/graph.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, Joyent, Inc. All rights reserved. */ /* @@ -141,6 +142,8 @@ #include <assert.h> #include <errno.h> #include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> #include <fm/libfmevent.h> #include <libscf.h> #include <libscf_priv.h> @@ -4875,6 +4878,20 @@ vertex_subgraph_dependencies_shutdown(scf_handle_t *h, graph_vertex_t *v, was_up = up_state(old_state); now_up = up_state(v->gv_state); + if (halting != -1 && old_state == RESTARTER_STATE_DISABLED && + v->gv_state != RESTARTER_STATE_DISABLED) { + /* + * We're halting and we have a svc which is transitioning to + * offline in parallel. This leads to a race condition where + * gt_enter_offline might re-enable the svc after we disabled + * it. Since we're halting, we want to ensure no svc ever + * transitions out of the disabled state. In this case, modify + * the flags to keep us on the halting path. + */ + was_up = 0; + now_up = 0; + } + if (!was_up && now_up) { ++non_subgraph_svcs; } else if (was_up && !now_up) { @@ -6827,6 +6844,7 @@ repository_event_thread(void *unused) char *fmri = startd_alloc(max_scf_fmri_size); char *pg_name = startd_alloc(max_scf_value_size); int r; + int fd; h = libscf_handle_create_bound_loop(); @@ -6849,6 +6867,14 @@ retry: goto retry; } + if ((fd = open("/etc/svc/volatile/startd.ready", O_RDONLY | O_CREAT, + S_IRUSR)) < 0) { + log_error(LOG_WARNING, "Couldn't create startd.ready file\n", + SCF_GROUP_FRAMEWORK, scf_strerror(scf_error())); + } else { + (void) close(fd); + } + /*CONSTCOND*/ while (1) { ssize_t res; diff --git a/usr/src/cmd/svc/startd/method.c b/usr/src/cmd/svc/startd/method.c index cc9ce6768c..c3cd0144c1 100644 --- a/usr/src/cmd/svc/startd/method.c +++ b/usr/src/cmd/svc/startd/method.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2011 Joyent Inc. + * Copyright 2012 Joyent, Inc. All rights reserved. */ /* @@ -100,34 +100,18 @@ static uint_t method_events[] = { * method_record_start(restarter_inst_t *) * Record a service start for rate limiting. Place the current time * in the circular array of instance starts. + * + * Save the critical_failure_period and critical_failure_allowed with either + * the defaults or the svc properties startd/critical_failure_count and + * startd/critical_failure_period. + * ri_crit_fail_allowed is capped at RINST_START_TIMES. */ static void method_record_start(restarter_inst_t *inst) { - int index = inst->ri_start_index++ % RINST_START_TIMES; - - inst->ri_start_time[index] = gethrtime(); -} - -/* - * method_rate_critical(restarter_inst_t *) - * Return true if the average start interval is less than the permitted - * interval. The implicit interval defaults to RINST_FAILURE_RATE_NS and - * RINST_START_TIMES but may be overridden with the svc properties - * startd/critical_failure_count and startd/critical_failure_period - * which represent the number of failures to consider and the amount of - * time in seconds in which that number may occur, respectively. Note that - * this time is measured as of the transition to 'enabled' rather than wall - * clock time. - * Implicit success if insufficient measurements for an average exist. - */ -int -method_rate_critical(restarter_inst_t *inst) -{ + int index; + uint_t critical_failure_allowed = RINST_START_TIMES; hrtime_t critical_failure_period; - uint_t critical_failure_count = RINST_START_TIMES; - uint_t n = inst->ri_start_index; - hrtime_t avg_ns = 0; uint64_t scf_fr, scf_st; scf_propvec_t *prop = NULL; scf_propvec_t restart_critical[] = { @@ -151,17 +135,48 @@ method_rate_critical(restarter_inst_t *inst) * in seconds but tracked in ns */ critical_failure_period = (hrtime_t)scf_fr * NANOSEC; - critical_failure_count = (uint_t)scf_st; + critical_failure_allowed = (uint_t)scf_st; + + if (critical_failure_allowed > RINST_START_TIMES) + critical_failure_allowed = RINST_START_TIMES; + if (critical_failure_allowed < 1) + critical_failure_allowed = 1; + } - if (inst->ri_start_index < critical_failure_count) + + inst->ri_crit_fail_allowed = critical_failure_allowed; + inst->ri_crit_fail_period = critical_failure_period; + + index = inst->ri_start_index++ % critical_failure_allowed; + inst->ri_start_time[index] = gethrtime(); +} + +/* + * method_rate_critical(restarter_inst_t *) + * Return true if the number of failures within the interval + * ri_crit_fail_period exceeds ri_crit_fail_allowed. The allowed failure + * count defaults to RINST_START_TIMES and the implicit interval defaults + * to RINST_FAILURE_RATE_NS but may be overridden with the svc properties + * startd/critical_failure_count and startd/critical_failure_period which + * represent the acceptable number of failures and the amount of time in + * seconds in which that number may occur, respectively. Note that this time + * is measured as of the transition to 'enabled' rather than wall clock + * time. Implicitly not critical if insufficient failures have occured. + */ +int +method_rate_critical(restarter_inst_t *inst) +{ + uint_t n = inst->ri_start_index; + uint_t fail_allowed = inst->ri_crit_fail_allowed; + hrtime_t diff_ns; + + if (n < fail_allowed) return (0); - avg_ns = - (inst->ri_start_time[(n - 1) % critical_failure_count] - - inst->ri_start_time[n % critical_failure_count]) / - (critical_failure_count - 1); + diff_ns = inst->ri_start_time[(n - 1) % fail_allowed] - + inst->ri_start_time[n % fail_allowed]; - return (avg_ns < critical_failure_period); + return (diff_ns < inst->ri_crit_fail_period); } /* @@ -989,7 +1004,8 @@ method_run(restarter_inst_t **instp, int type, int *exit_code) goto contract_out; } - if (!WIFEXITED(ret_status)) { + if (!WIFEXITED(ret_status) && + WEXITSTATUS(ret_status) != SMF_EXIT_NODAEMON) { /* * If method didn't exit itself (it was killed by an * external entity, etc.), consider the entire @@ -1018,7 +1034,7 @@ method_run(restarter_inst_t **instp, int type, int *exit_code) } *exit_code = WEXITSTATUS(ret_status); - if (*exit_code != 0) { + if (*exit_code != 0 && *exit_code != SMF_EXIT_NODAEMON) { log_error(LOG_WARNING, "%s: Method \"%s\" failed with exit status %d.\n", inst->ri_i.i_fmri, method, WEXITSTATUS(ret_status)); @@ -1027,6 +1043,7 @@ method_run(restarter_inst_t **instp, int type, int *exit_code) log_instance(inst, B_TRUE, "Method \"%s\" exited with status " "%d.", mname, *exit_code); + /* Note: we will take this path for SMF_EXIT_NODAEMON */ if (*exit_code != 0) goto contract_out; @@ -1073,7 +1090,10 @@ assured_kill: } contract_out: - /* Abandon contracts for transient methods & methods that fail. */ + /* + * Abandon contracts for transient methods, methods that exit with + * SMF_EXIT_NODAEMON & methods that fail. + */ transient = method_is_transient(inst, type); if ((transient || *exit_code != 0 || result != 0) && (restarter_is_kill_method(method) < 0)) @@ -1169,7 +1189,7 @@ retry: r = method_run(&inst, info->sf_method_type, &exit_code); - if (r == 0 && exit_code == 0) { + if (r == 0 && (exit_code == 0 || exit_code == SMF_EXIT_NODAEMON)) { /* Success! */ assert(inst->ri_i.i_next_state != RESTARTER_STATE_NONE); @@ -1187,6 +1207,12 @@ retry: else method_remove_contract(inst, B_TRUE, B_TRUE); } + + /* + * For methods that exit with SMF_EXIT_NODAEMON, we already + * called method_remove_contract in method_run. + */ + /* * We don't care whether the handle was rebound because this is * the last thing we do with it. diff --git a/usr/src/cmd/svc/startd/startd.c b/usr/src/cmd/svc/startd/startd.c index 6e3ea9876b..c3705c0c5c 100644 --- a/usr/src/cmd/svc/startd/startd.c +++ b/usr/src/cmd/svc/startd/startd.c @@ -42,6 +42,136 @@ * engine commands by executing methods, updating the repository, and sending * feedback (mostly state updates) to the graph engine. * + * Overview of the SMF Architecture + * + * There are a few different components that make up SMF and are responsible + * for different pieces of functionality that are used: + * + * svc.startd(1M): A daemon that is in charge of starting, stopping, and + * restarting services and instances + * svc.configd: A daemon that manages the repository that stores information, + * property groups, and state of the different services and instances + * libscf(3LIB): A C library that provides the glue for communicating, + * accessing, and updating information about services and instances + * svccfg(1M): A utility to add and remove services as well as change the + * properties associated with different services and instances. + * svcadm(1M): A utility to control the different instance of a service. You + * can use this to enable and disable them among some other useful things. + * svcs(1): A utility that reports on the status of various services on the + * system + * + * The following block diagram explains how these components communicate: + * + * The SMF Block Diagram + * Repository + * This attempts to show ___________ __________ + * the relations between | | SQL | | + * the different pieces | configd |<----------->| SQLite | + * that make SMF work and | | Transaction | | + * users/administrators ----------- ---------- + * call into. /|\ /|\ + * | | + * door_call(3C)| | door_call(3C) + * | | + * \|/ \|/ + * ____________ __________ __________ ____________ + * | | | | | | | svccfg | + * | startd |<--->| libscf | | libscf |<---->| svcadm | + * | | | (3LIB) | | (3LIB) | | svcs | + * ------------ ---------- ---------- ------------ + * /|\ /|\ + * | | fork(2)/exec(2) + * | | libcontract(3LIB) + * \|/ \|/ Various System/User services + * --------------------------------------------------------------------- + * | system/filesystem/local:default system/coreadm:default | + * | network/lookpback:default system/zones:default | + * | network/ntp:default system/cron:default | + * | smartdc/agent/ca/cainstsvc:default network/ssh:default | + * | appliance/kit/akd:default system/svc/restarter:default | + * --------------------------------------------------------------------- + * + * Chatting with configd and sharing repository information + * + * As you run commands with svcs, svccfg, and svcadm, they are all creating a + * libscf handle to communicate with configd. As calls are made via libscf they + * ultimately go and talk to configd to get information. However, how we + * actually are talking to configd is not as straightforward as it appears. + * + * When configd starts up it creates a door located at + * /etc/svc/volatile/repository_door. This door runs the routine called + * main_switcher() from usr/src/cmd/svc/configd/maindoor.c. When you first + * invoke svc(cfg|s|adm), one of the first things that occurs is creating a + * scf_handle_t and binding it to configd by calling scf_handle_bind(). This + * function makes a door call to configd and gets returned a new file + * descriptor. This file descriptor is itself another door which calls into + * configd's client_switcher(). This is the door that is actually used when + * getting and fetching properties, and many other useful things. + * + * svc.startd needs a way to notice the changes that occur to the repository. + * For example, if you enabled a service that was not previously running, it's + * up to startd to notice that this has happened, check dependencies, and + * eventually start up the service. The way it gets these notifications is via + * a thread who's sole purpose in life is to call _scf_notify_wait(). This + * function acts like poll(2) but for changes that occur in the repository. + * Once this thread gets the event, it dispatches the event appropriately. + * + * The Events of svc.startd + * + * svc.startd has to handle a lot of complexity. Understanding how you go from + * getting the notification that a service was enabled to actually enabling it + * is not obvious from a cursory glance. The first thing to keep in mind is + * that startd maintains a graph of all the related services and instances so + * it can keep track of what is enabled, what dependencies exist, etc. all so + * that it can answer the question of what is affected by a change. Internally + * there are a lot of different queues for events, threads to process these + * queues, and different paths to have events enter these queues. What follows + * is a diagram that attempts to explain some of those paths, though it's + * important to note that for some of these pieces, such as the graph and + * vertex events, there are many additional ways and code paths these threads + * and functions can take. And yes, restarter_event_enqueue() is not the same + * thing as restarter_queue_event(). + * + * Threads/Functions Queues Threads/Functions + * + * called by various + * ------------------ --------- --------------- + * --->| graph_protocol | graph_event | graph | graph_event_ | graph_event | + * --->| _send_event() |------------>| event |----------------->| _thread | + * ------------------ _enqueue() | queue | dequeue() --------------- + * --------- | + * _scf_notify_wait() vertex_send_event()| + * | \|/ + * | -------------------- ---------------------- + * |->| repository_event | vertex_send_event() | restarter_protocol | + * | _thread |----------------------------->| _send_event() | + * -------------------- ---------------------- + * | | out to other + * restarter_ restarter_ | | restarters + * event_dequeue() ------------- event_ | | not startd + * |----------------| restarter |<------------| |-------------> + * \|/ | event | enqueue() + * ------------------- | queue | |------------------> + * | restarter_event | ------------- ||-----------------> + * | _thread | |||----------------> + * ------------------- ||| start/stop inst + * | ---------------- ---------------------- + * | | instance | | restarter_process_ | + * |-------------->| event |------>| events | + * restarter_ | queue | | per-instance lwp | + * queue_event() ---------------- ---------------------- + * ||| various funcs + * ||| controlling + * ||| instance state + * |||---------------> + * ||----------------> + * |-----------------> + * + * What's important to take away is that there is a queue for each instance on + * the system that handles events related to dealing directly with that + * instance and that events can be added to it because of changes to properties + * that are made to configd and acted upon asynchronously by startd. + * * Error handling * * In general, when svc.startd runs out of memory it reattempts a few times, diff --git a/usr/src/cmd/svc/startd/startd.h b/usr/src/cmd/svc/startd/startd.h index c1062e45e0..e204fb829f 100644 --- a/usr/src/cmd/svc/startd/startd.h +++ b/usr/src/cmd/svc/startd/startd.h @@ -405,7 +405,7 @@ typedef enum { #define RINST_RETAKE_MASK 0x0f000000 -#define RINST_START_TIMES 5 /* failures to consider */ +#define RINST_START_TIMES 10 /* up to 10 fails to consider */ #define RINST_FAILURE_RATE_NS 600000000000LL /* 1 failure/10 minutes */ #define RINST_WT_SVC_FAILURE_RATE_NS NANOSEC /* 1 failure/second */ @@ -427,6 +427,8 @@ typedef struct restarter_inst { hrtime_t ri_start_time[RINST_START_TIMES]; uint_t ri_start_index; /* times started */ + uint_t ri_crit_fail_allowed; + hrtime_t ri_crit_fail_period; uu_list_node_t ri_link; pthread_mutex_t ri_lock; diff --git a/usr/src/cmd/svc/svcadm/Makefile b/usr/src/cmd/svc/svcadm/Makefile index cc0cc160bf..1a6a0dd35c 100644 --- a/usr/src/cmd/svc/svcadm/Makefile +++ b/usr/src/cmd/svc/svcadm/Makefile @@ -21,6 +21,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012, Joyent, Inc. All rights reserved. # PROG = svcadm @@ -49,7 +50,11 @@ $(PROG): $(OBJS) $(POFILE): $(POFILES) cat $(POFILES) > $(POFILE) -install: all $(ROOTUSRSBINPROG) +install: all $(ROOTSBINPROG) $(ROOTUSRSBINPROG) + +$(ROOTUSRSBINPROG): + -$(RM) $@ + -$(SYMLINK) ../../sbin/$(PROG) $@ clean: $(RM) $(OBJS) diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c index 5a96e5eac4..077a77f114 100644 --- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c +++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. */ @@ -44,6 +45,7 @@ #include <stdarg.h> #include <string.h> #include <strings.h> +#include <time.h> #include <unistd.h> #include <wait.h> #include <poll.h> @@ -241,6 +243,9 @@ static const char *emsg_dpt_no_dep; static int li_only = 0; static int no_refresh = 0; +/* how long in ns we should wait between checks for a pg */ +static uint64_t pg_timeout = 100 * (NANOSEC / MILLISEC); + /* import globals, to minimize allocations */ static scf_scope_t *imp_scope = NULL; static scf_service_t *imp_svc = NULL, *imp_tsvc = NULL; @@ -6751,6 +6756,205 @@ connaborted: } /* + * When an instance is imported we end up telling configd about it. Once we tell + * configd about these changes, startd eventually notices. If this is a new + * instance, the manifest may not specify the SCF_PG_RESTARTER (restarter) + * property group. However, many of the other tools expect that this property + * group exists and has certain values. + * + * These values are added asynchronously by startd. We should not return from + * this routine until we can verify that the property group we need is there. + * + * Before we go ahead and verify this, we have to ask ourselves an + * important question: Is the early manifest service currently running? + * Because if is running and invoked us, then the service will never get + * a restarter property because svc.startd is blocked on EMI finishing + * before it lets itself fully connect to svc.configd. Of course, this + * means that this race condition is in fact impossible to 100% + * eliminate. + * + * svc.startd makes sure that EMI only runs once and has succeeded by + * checking the state of the EMI instance. If it is online it bails out + * and makes sure that it doesn't run again. In this case, we're going + * to do something similar, only if the state is online, then we're + * going to actually verify. EMI always has to be present, but it + * can be explicitly disabled to reduce the amount of damage it can cause. If + * EMI has been disabled then we no longer have to worry about the implicit race + * condition and can go ahead and check things. If EMI is in some tate that + * isn't online or disabled and isn't runinng, then we assume that things are + * rather bad and we're not going to get in your way, even if the rest of SMF + * does. + * + * Returns 0 on success or returns an errno. + */ +#ifndef NATIVE_BUILD +static int +lscf_instance_verify(scf_scope_t *scope, entity_t *svc, entity_t *inst) +{ + int ret, err; + struct timespec ts; + char *emi_state; + + /* + * smf_get_state does not distinguish between its different failure + * modes: memory allocation failures and SMF internal failures. + */ + if ((emi_state = smf_get_state(SCF_INSTANCE_EMI)) == NULL) + return (EAGAIN); + + /* + * As per the block comment for this function check the state of EMI + */ + if (strcmp(emi_state, SCF_STATE_STRING_ONLINE) != 0 && + strcmp(emi_state, SCF_STATE_STRING_DISABLED) != 0) { + warn(gettext("Not validating instance %s:%s because EMI's " + "state is %s\n"), svc->sc_name, inst->sc_name, emi_state); + free(emi_state); + return (0); + } + + free(emi_state); + + /* + * First we have to get the property. + */ + if ((ret = scf_scope_get_service(scope, svc->sc_name, imp_svc)) != 0) { + ret = scf_error(); + warn(gettext("Failed to look up service: %s\n"), svc->sc_name); + return (ret); + } + + /* + * We should always be able to get the instance. It should already + * exist because we just created it or got it. There probably is a + * slim chance that someone may have come in and deleted it though from + * under us. + */ + if ((ret = scf_service_get_instance(imp_svc, inst->sc_name, imp_inst)) + != 0) { + ret = scf_error(); + warn(gettext("Failed to verify instance: %s\n"), inst->sc_name); + switch (ret) { + case SCF_ERROR_DELETED: + err = ENODEV; + break; + case SCF_ERROR_CONNECTION_BROKEN: + warn(gettext("Lost repository connection\n")); + err = ECONNABORTED; + break; + case SCF_ERROR_NOT_FOUND: + warn(gettext("Instance \"%s\" disappeared out from " + "under us.\n"), inst->sc_name); + err = ENOENT; + break; + default: + bad_error("scf_service_get_instance", ret); + } + + return (err); + } + + /* + * An astute observer may want to use _scf_wait_pg which would notify us + * of a property group change, unfortunately that does not work if the + * property group in question does not exist. So instead we have to + * manually poll and ask smf the best way to get to it. + */ + while ((ret = scf_instance_get_pg(imp_inst, SCF_PG_RESTARTER, imp_pg)) + != SCF_SUCCESS) { + ret = scf_error(); + if (ret != SCF_ERROR_NOT_FOUND) { + warn(gettext("Failed to get restarter property " + "group for instance: %s\n"), inst->sc_name); + switch (ret) { + case SCF_ERROR_DELETED: + err = ENODEV; + break; + case SCF_ERROR_CONNECTION_BROKEN: + warn(gettext("Lost repository connection\n")); + err = ECONNABORTED; + break; + default: + bad_error("scf_service_get_instance", ret); + } + + return (err); + } + + ts.tv_sec = pg_timeout / NANOSEC; + ts.tv_nsec = pg_timeout % NANOSEC; + + (void) nanosleep(&ts, NULL); + } + + /* + * svcadm also expects that the SCF_PROPERTY_STATE property is present. + * So in addition to the property group being present, we need to wait + * for the property to be there in some form. + * + * Note that a property group is a frozen snapshot in time. To properly + * get beyond this, you have to refresh the property group each time. + */ + while ((ret = scf_pg_get_property(imp_pg, SCF_PROPERTY_STATE, + imp_prop)) != 0) { + + ret = scf_error(); + if (ret != SCF_ERROR_NOT_FOUND) { + warn(gettext("Failed to get property %s from the " + "restarter property group of instance %s\n"), + SCF_PROPERTY_STATE, inst->sc_name); + switch (ret) { + case SCF_ERROR_CONNECTION_BROKEN: + warn(gettext("Lost repository connection\n")); + err = ECONNABORTED; + break; + case SCF_ERROR_DELETED: + err = ENODEV; + break; + default: + bad_error("scf_pg_get_property", ret); + } + + return (err); + } + + ts.tv_sec = pg_timeout / NANOSEC; + ts.tv_nsec = pg_timeout % NANOSEC; + + (void) nanosleep(&ts, NULL); + + ret = scf_instance_get_pg(imp_inst, SCF_PG_RESTARTER, imp_pg); + if (ret != SCF_SUCCESS) { + warn(gettext("Failed to get restarter property " + "group for instance: %s\n"), inst->sc_name); + switch (ret) { + case SCF_ERROR_DELETED: + err = ENODEV; + break; + case SCF_ERROR_CONNECTION_BROKEN: + warn(gettext("Lost repository connection\n")); + err = ECONNABORTED; + break; + default: + bad_error("scf_service_get_instance", ret); + } + + return (err); + } + } + + /* + * We don't have to free the property groups or other values that we got + * because we stored them in global variables that are allocated and + * freed by the routines that call into these functions. Unless of + * course the rest of the code here that we are basing this on is + * mistaken. + */ + return (0); +} +#endif + +/* * If the service is missing, create it, import its properties, and import the * instances. Since the service is brand new, it should be empty, and if we * run into any existing entities (SCF_ERROR_EXISTS), abort. @@ -6834,6 +7038,7 @@ lscf_service_import(void *v, void *pvt) int fresh = 0; scf_snaplevel_t *running; int have_ge = 0; + boolean_t retried = B_FALSE; const char * const ts_deleted = gettext("Temporary service svc:/%s " "was deleted unexpectedly.\n"); @@ -6889,6 +7094,7 @@ lscf_service_import(void *v, void *pvt) return (UU_WALK_ERROR); } +retry: if (scf_scope_add_service(imp_scope, imp_tsname, imp_tsvc) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: @@ -6898,6 +7104,11 @@ lscf_service_import(void *v, void *pvt) return (stash_scferror(lcbdata)); case SCF_ERROR_EXISTS: + if (!retried) { + lscf_delete(imp_tsname, 0); + retried = B_TRUE; + goto retry; + } warn(gettext( "Temporary service \"%s\" must be deleted before " "this manifest can be imported.\n"), imp_tsname); @@ -8122,7 +8333,36 @@ lscf_bundle_import(bundle_t *bndl, const char *filename, uint_t flags) goto progress; result = 0; + + /* + * This snippet of code assumes that we are running svccfg as we + * normally do -- witih svc.startd running. Of course, that is + * not actually the case all the time because we also use a + * varient of svc.configd and svcccfg which are only meant to + * run during the build process. During this time we have no + * svc.startd, so this check would hang the build process. + */ +#ifndef NATIVE_BUILD + /* + * Verify that the restarter group is preset + */ + for (svc = uu_list_first(bndl->sc_bundle_services); + svc != NULL; + svc = uu_list_next(bndl->sc_bundle_services, svc)) { + + insts = svc->sc_u.sc_service.sc_service_instances; + + for (inst = uu_list_first(insts); + inst != NULL; + inst = uu_list_next(insts, inst)) { + if (lscf_instance_verify(imp_scope, svc, + inst) != 0) + goto progress; + } + } +#endif goto out; + } if (uu_error() != UU_ERROR_CALLBACK_FAILED) diff --git a/usr/src/cmd/svc/svcs/Makefile b/usr/src/cmd/svc/svcs/Makefile index 0e9fc52652..2ea89818c0 100644 --- a/usr/src/cmd/svc/svcs/Makefile +++ b/usr/src/cmd/svc/svcs/Makefile @@ -34,7 +34,7 @@ include ../../Makefile.cmd include ../../Makefile.ctf POFILE = $(PROG)_all.po -LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg +LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg -lsasl CPPFLAGS += -I ../common lint := LINTFLAGS = -mux diff --git a/usr/src/cmd/svc/svcs/explain.c b/usr/src/cmd/svc/svcs/explain.c index 42fca80172..eed9733abc 100644 --- a/usr/src/cmd/svc/svcs/explain.c +++ b/usr/src/cmd/svc/svcs/explain.c @@ -200,6 +200,7 @@ static char *emsg_invalid_dep; extern scf_handle_t *h; extern char *g_zonename; +extern char *g_zonealias; /* ARGSUSED */ static int @@ -2000,6 +2001,9 @@ print_service(inst_t *svcp, int verbose) if (g_zonename != NULL) (void) printf(gettext(" Zone: %s\n"), g_zonename); + if (g_zonealias != NULL) + (void) printf(gettext(" Alias: %s\n"), g_zonealias); + stime = svcp->stime.tv_sec; tmp = localtime(&stime); diff --git a/usr/src/cmd/svc/svcs/svcs.c b/usr/src/cmd/svc/svcs/svcs.c index c54f4bd12d..b4882d1776 100644 --- a/usr/src/cmd/svc/svcs/svcs.c +++ b/usr/src/cmd/svc/svcs/svcs.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* @@ -59,6 +59,7 @@ #include <sys/ctfs.h> #include <sys/stat.h> +#include <sasl/saslutil.h> #include <assert.h> #include <errno.h> #include <fcntl.h> @@ -134,6 +135,9 @@ static int first_paragraph = 1; /* For -l mode. */ static char *common_name_buf; /* Sized for maximal length value. */ char *locale; /* Current locale. */ char *g_zonename; /* zone being operated upon */ +char *g_zonealias; /* alias for zone, if any */ +static char g_aliasdec[MAXPATHLEN / 4 * 3]; /* decoded zone alias buffer */ +static char g_aliasbuf[MAXPATHLEN]; /* base64 encoded zone alias buffer */ /* * Pathname storage for path generated from the fmri. @@ -240,7 +244,25 @@ ht_free(void) static void ht_init(void) { - assert(ht_buckets == NULL); + if (ht_buckets != NULL) { + /* + * If we already have a hash table (e.g., because we are + * processing multiple zones), destroy it before creating + * a new one. + */ + struct ht_elem *elem, *next; + int i; + + for (i = 0; i < ht_buckets_num; i++) { + for (elem = ht_buckets[i]; elem != NULL; elem = next) { + next = elem->next; + free((char *)elem->fmri); + free(elem); + } + } + + free(ht_buckets); + } ht_buckets_num = 8; ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num); @@ -553,7 +575,7 @@ get_restarter_time_prop(scf_instance_t *inst, const char *pname, int r; r = inst_get_single_val(inst, SCF_PG_RESTARTER, pname, SCF_TYPE_TIME, - tvp, NULL, ok_if_empty ? EMPTY_OK : 0, 0, 1); + tvp, 0, ok_if_empty ? EMPTY_OK : 0, 0, 1); return (r == 0 ? 0 : -1); } @@ -1650,7 +1672,7 @@ sprint_stime(char **buf, scf_walkinfo_t *wip) SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); } else { r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, - SCF_TYPE_TIME, &tv, NULL, 0); + SCF_TYPE_TIME, &tv, 0, 0); } if (r != 0) { @@ -1702,7 +1724,7 @@ sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip) SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0); else r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP, - SCF_TYPE_TIME, &tv, NULL, 0); + SCF_TYPE_TIME, &tv, 0, 0); if (r == 0) { int64_t sec; @@ -2514,7 +2536,7 @@ print_detailed(void *unused, scf_walkinfo_t *wip) gettext("next_state"), buf); if (pg_get_single_val(rpg, SCF_PROPERTY_STATE_TIMESTAMP, - SCF_TYPE_TIME, &tv, NULL, 0) == 0) { + SCF_TYPE_TIME, &tv, 0, 0) == 0) { stime = tv.tv_sec; tmp = localtime(&stime); for (tbsz = 50; ; tbsz *= 2) { @@ -3663,6 +3685,24 @@ again: assert(opt_zone == NULL || zids == NULL); if (opt_zone == NULL) { + zone_status_t status; + + if (zone_getattr(zids[zent], ZONE_ATTR_STATUS, + &status, sizeof (status)) < 0 || + status != ZONE_IS_RUNNING) { + /* + * If this zone is not running or we cannot + * get its status, we do not want to attempt + * to bind an SCF handle to it, lest we + * accidentally interfere with a zone that + * is not yet running by looking up a door + * to its svc.configd (which could potentially + * block a mount with an EBUSY). + */ + zent++; + goto nextzone; + } + if (getzonenamebyid(zids[zent++], zonename, sizeof (zonename)) < 0) { uu_warn(gettext("could not get name for " @@ -3685,18 +3725,46 @@ again: uu_die(gettext("invalid zone '%s'\n"), g_zonename); scf_value_destroy(zone); + + /* + * On SmartOS, there may be a base64-encoded string attribute + * named 'alias' associated with this zone. This alias is + * useful, so we attempt to make it available when we are + * displaying -xZ output. If it's not available or not + * decodable, we just ignore it. + */ + if (g_zonename != NULL) { + unsigned len; + struct zone_attrtab zattrs; + zone_dochandle_t zhdl = zonecfg_init_handle(); + + bzero(&zattrs, sizeof (zattrs)); + (void) strcpy(zattrs.zone_attr_name, "alias"); + + if (zhdl != NULL && + zonecfg_get_handle(g_zonename, zhdl) == Z_OK && + zonecfg_lookup_attr(zhdl, &zattrs) == Z_OK && + zonecfg_get_attr_string(&zattrs, g_aliasbuf, + sizeof (g_aliasbuf)) == Z_OK && + sasl_decode64(g_aliasbuf, strlen(g_aliasbuf), + g_aliasdec, sizeof (g_aliasdec), &len) == SASL_OK) { + g_aliasdec[len] = '\0'; + g_zonealias = g_aliasdec; + } else { + g_zonealias = NULL; + } + zonecfg_fini_handle(zhdl); + } } if (scf_handle_bind(h) == -1) { if (g_zonename != NULL) { - uu_warn(gettext("Could not bind to repository " + if (show_zones) + goto nextzone; + + uu_die(gettext("Could not bind to repository " "server for zone %s: %s\n"), g_zonename, scf_strerror(scf_error())); - - if (!show_zones) - return (UU_EXIT_FATAL); - - goto nextzone; } uu_die(gettext("Could not bind to repository server: %s. " @@ -3755,7 +3823,7 @@ again: if (opt_mode == 'L') { if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, - print_log, NULL, &exit_status, uu_warn)) != 0) { + print_log, NULL, errarg, errfunc)) != 0) { uu_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; diff --git a/usr/src/cmd/svr4pkg/pkgadd/Makefile b/usr/src/cmd/svr4pkg/pkgadd/Makefile index 66e6abb737..b1b4168a7c 100644 --- a/usr/src/cmd/svr4pkg/pkgadd/Makefile +++ b/usr/src/cmd/svr4pkg/pkgadd/Makefile @@ -35,7 +35,7 @@ ROOTLINKS= $(ROOTUSRSBIN)/pkgask include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg LDLIBS += -lpkg -linstzones -ladm -LDLIBS += -lcrypto -lwanboot +LDLIBS += -lsunw_crypto -lwanboot .KEEP_STATE: diff --git a/usr/src/cmd/svr4pkg/pkgadm/Makefile b/usr/src/cmd/svr4pkg/pkgadm/Makefile index 620e32cf0d..c4970c3b91 100644 --- a/usr/src/cmd/svr4pkg/pkgadm/Makefile +++ b/usr/src/cmd/svr4pkg/pkgadm/Makefile @@ -35,7 +35,7 @@ OBJS= addcert.o \ include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg -LDLIBS += -lpkg -ladm -lcrypto -lgen +LDLIBS += -lpkg -ladm -lsunw_crypto -lgen .KEEP_STATE: all: $(PROG) diff --git a/usr/src/cmd/tail/Makefile b/usr/src/cmd/tail/Makefile index 293920cfd1..e660cedf2d 100644 --- a/usr/src/cmd/tail/Makefile +++ b/usr/src/cmd/tail/Makefile @@ -21,6 +21,7 @@ OBJS= forward.o misc.o read.o reverse.o tail.o SRCS= $(OBJS:%.o=%.c) include ../Makefile.cmd +include ../Makefile.ctf CLOBBERFILES= $(PROG) CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 @@ -43,6 +44,10 @@ $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + install: all .WAIT $(ROOTPROG) $(ROOTXPG4PROG) $(ROOTXPG4PROG): diff --git a/usr/src/cmd/tar/Makefile b/usr/src/cmd/tar/Makefile index 5da69ec0f9..93a02e58e0 100644 --- a/usr/src/cmd/tar/Makefile +++ b/usr/src/cmd/tar/Makefile @@ -37,8 +37,6 @@ LINTFLAGS += -u LDLIBS += -lsec -lcmdutils -lnvpair -ltsol CFLAGS += $(CCVERBOSE) -CERRWARN += -_gcc=-Wno-unused-variable -CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-uninitialized CPPFLAGS += -DEUC diff --git a/usr/src/cmd/tar/tar.c b/usr/src/cmd/tar/tar.c index 9e2aef3bee..7b48e927e1 100644 --- a/usr/src/cmd/tar/tar.c +++ b/usr/src/cmd/tar/tar.c @@ -565,7 +565,7 @@ static char *myname; static char *xtract_chdir = NULL; static int checkflag = 0; static int Xflag, Fflag, iflag, hflag, Bflag, Iflag; -static int rflag, xflag, vflag, tflag, mt, svmt, cflag, mflag, pflag; +static int rflag, xflag, vflag, tflag, mt, cflag, mflag, pflag; static int uflag; static int errflag; static int oflag; @@ -643,6 +643,8 @@ static int charset_type = 0; static u_longlong_t xhdr_flgs; /* Bits set determine which items */ /* need to be in extended header. */ +static pid_t comp_pid = 0; + #define _X_DEVMAJOR 0x1 #define _X_DEVMINOR 0x2 #define _X_GID 0x4 @@ -725,8 +727,6 @@ main(int argc, char *argv[]) char *cp; char *tmpdirp; pid_t thispid; - pid_t pid; - int wstat; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ @@ -1114,10 +1114,8 @@ main(int argc, char *argv[]) if (Aflag && vflag) (void) printf( gettext("Suppressing absolute pathnames\n")); - if (cflag && compress_opt != NULL) { - pid = compress_file(); - wait_pid(pid); - } + if (cflag && compress_opt != NULL) + comp_pid = compress_file(); dorep(argv); if (rflag && !cflag && (compress_opt != NULL)) compress_back(); @@ -1168,10 +1166,8 @@ main(int argc, char *argv[]) if (strcmp(usefile, "-") != 0) { check_compression(); - if (compress_opt != NULL) { - pid = uncompress_file(); - wait_pid(pid); - } + if (compress_opt != NULL) + comp_pid = uncompress_file(); } if (xflag) { if (xtract_chdir != NULL) { @@ -4876,6 +4872,13 @@ done(int n) exit(2); } } + /* + * If we have a compression child, we should have a child process that + * we're waiting for to finish compressing or uncompressing the tar + * stream. + */ + if (n == 0 && comp_pid != 0) + wait_pid(comp_pid); exit(n); } @@ -6109,7 +6112,6 @@ check_prefix(char **namep, char **dirp, char **compp) if ((tflag || xflag) && !Pflag) { if (is_absolute(fullname) || has_dot_dot(fullname)) { char *stripped_prefix; - size_t prefix_len = 0; (void) strcpy(savename, fullname); strcpy(fullname, @@ -7891,7 +7893,7 @@ xattrs_put(char *longname, char *shortname, char *parent, char *attrparent) return; } - while (dp = readdir(dirp)) { + while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name, "..") == 0) { continue; } else if (strcmp(dp->d_name, ".") == 0) { @@ -9191,9 +9193,6 @@ static void compress_back() { pid_t pid; - int status; - int wret; - struct stat statb; if (vflag) { (void) fprintf(vfile, @@ -9299,9 +9298,6 @@ void decompress_file(void) { pid_t pid; - int status; - char cmdstr[PATH_MAX]; - char fname[PATH_MAX]; char *added_suffix; @@ -9344,7 +9340,7 @@ compress_file(void) if (pipe(fd) < 0) { vperror(1, gettext("Could not create pipe")); } - if (pid = fork() > 0) { + if ((pid = fork()) > 0) { mt = fd[1]; (void) close(fd[0]); return (pid); @@ -9373,7 +9369,7 @@ uncompress_file(void) if (pipe(fd) < 0) { vperror(1, gettext("Could not create pipe")); } - if (pid = fork() > 0) { + if ((pid = fork()) > 0) { mt = fd[0]; (void) close(fd[1]); return (pid); diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index 069268dc05..303b652b91 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -23,7 +23,7 @@ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. All rights reserved. * Copyright (c) 2014, OmniTI Computer Consulting, Inc. All rights reserved. */ @@ -108,7 +108,7 @@ #include "proto.h" #define FCNTLMIN F_DUPFD -#define FCNTLMAX F_BADFD +#define FCNTLMAX F_FLOCKW const char *const FCNTLname[] = { "F_DUPFD", "F_GETFD", @@ -156,7 +156,15 @@ const char *const FCNTLname[] = { "F_SHARE_NBMAND", "F_SETLK64_NBMAND", NULL, /* 45 */ - "F_BADFD" + "F_BADFD", + "F_OFD_GETLK", + "F_OFD_SETLK", + "F_OFD_SETLKW", + NULL, /* 50 */ + NULL, /* 51 */ + NULL, /* 52 */ + "F_FLOCK", + "F_FLOCKW" }; #define SYSFSMIN GETFSIND @@ -703,6 +711,8 @@ const struct ioc { /* /dev/poll ioctl() control codes */ { (uint_t)DP_POLL, "DP_POLL", NULL }, { (uint_t)DP_ISPOLLED, "DP_ISPOLLED", NULL }, + { (uint_t)DP_PPOLL, "DP_PPOLL", NULL }, + { (uint_t)DP_EPOLLCOMPAT, "DP_EPOLLCOMPAT", NULL }, /* the old /proc ioctl() control codes */ #define PIOC ('q'<<8) { (uint_t)(PIOC|1), "PIOCSTATUS", NULL }, diff --git a/usr/src/cmd/truss/expound.c b/usr/src/cmd/truss/expound.c index 915ec4626b..6bfa2fd86f 100644 --- a/usr/src/cmd/truss/expound.c +++ b/usr/src/cmd/truss/expound.c @@ -23,6 +23,7 @@ * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright 2015 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -1872,6 +1873,11 @@ show_fcntl(private_t *pri) case F_FREESP: case F_ALLOCSP: case F_SETLK_NBMAND: + case F_OFD_GETLK: + case F_OFD_SETLK: + case F_OFD_SETLKW: + case F_FLOCK: + case F_FLOCKW: if (data_model == PR_MODEL_LP64) show_flock64(pri, offset); else @@ -1883,6 +1889,11 @@ show_fcntl(private_t *pri) case 27: /* F_FREESP64 */ case 28: /* F_ALLOCSP64 */ case 44: /* F_SETLK64_NBMAND */ + case 50: /* F_OFD_GETLK64 */ + case 51: /* F_OFD_SETLK64 */ + case 52: /* F_OFD_SETLKW64 */ + case 55: /* F_FLOCK64 */ + case 56: /* F_FLOCKW64 */ show_flock64(pri, offset); break; #else /* _LP64 */ @@ -1900,6 +1911,11 @@ show_fcntl(private_t *pri) case F_FREESP64: case F_ALLOCSP64: case F_SETLK64_NBMAND: + case F_OFD_GETLK64: + case F_OFD_SETLK64: + case F_OFD_SETLKW64: + case F_FLOCK64: + case F_FLOCKW64: show_flock64(pri, offset); break; #endif /* _LP64 */ diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c index 28e24117d4..66a9d7387e 100644 --- a/usr/src/cmd/truss/print.c +++ b/usr/src/cmd/truss/print.c @@ -845,6 +845,7 @@ prt_mad(private_t *pri, int raw, long val) /* print madvise() argument */ case MADV_ACCESS_DEFAULT: s = "MADV_ACCESS_DEFAULT"; break; case MADV_ACCESS_LWP: s = "MADV_ACCESS_LWP"; break; case MADV_ACCESS_MANY: s = "MADV_ACCESS_MANY"; break; + case MADV_PURGE: s = "MADV_PURGE"; break; } } @@ -872,7 +873,9 @@ prt_mc4(private_t *pri, int raw, long val) /* print memcntl() (4th) argument */ return; case MC_SYNC: - if ((val & ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE)) == 0) { + if ((val & + ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE|MS_INVALCURPROC)) + == 0) { *(s = pri->code_buf) = '\0'; if (val & MS_SYNC) (void) strlcat(s, "|MS_SYNC", CBSIZE); @@ -881,6 +884,9 @@ prt_mc4(private_t *pri, int raw, long val) /* print memcntl() (4th) argument */ if (val & MS_INVALIDATE) (void) strlcat(s, "|MS_INVALIDATE", CBSIZE); + if (val & MS_INVALCURPROC) + (void) strlcat(s, "|MS_INVALCURPROC", + CBSIZE); } break; @@ -1994,6 +2000,7 @@ udp_optname(private_t *pri, long val) case UDP_EXCLBIND: return ("UDP_EXCLBIND"); case UDP_RCVHDR: return ("UDP_RCVHDR"); case UDP_NAT_T_ENDPOINT: return ("UDP_NAT_T_ENDPOINT"); + case UDP_SRCPORT_HASH: return ("UDP_SRCPORT_HASH"); default: (void) snprintf(pri->code_buf, sizeof (pri->code_buf), "0x%lx", @@ -2442,7 +2449,10 @@ prt_zga(private_t *pri, int raw, long val) case ZONE_ATTR_BOOTARGS: s = "ZONE_ATTR_BOOTARGS"; break; case ZONE_ATTR_BRAND: s = "ZONE_ATTR_BRAND"; break; case ZONE_ATTR_FLAGS: s = "ZONE_ATTR_FLAGS"; break; - case ZONE_ATTR_PHYS_MCAP: s = "ZONE_ATTR_PHYS_MCAP"; break; + case ZONE_ATTR_DID: s = "ZONE_ATTR_DID"; break; + case ZONE_ATTR_PMCAP_NOVER: s = "ZONE_ATTR_PMCAP_NOVER"; break; + case ZONE_ATTR_PMCAP_PAGEOUT: s = "ZONE_ATTR_PMCAP_PAGEOUT"; + break; } } diff --git a/usr/src/cmd/truss/systable.c b/usr/src/cmd/truss/systable.c index febd7d71f5..8c736a7044 100644 --- a/usr/src/cmd/truss/systable.c +++ b/usr/src/cmd/truss/systable.c @@ -29,6 +29,9 @@ /* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */ +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> @@ -1716,9 +1719,10 @@ const char * const afcodes[] = { "POLICY", /* 29 */ "RDS", /* 30 */ "TRILL", /* 31 */ - "PACKET" /* 32 */ + "PACKET", /* 32 */ + "LX_NETLINK" /* 33 */ }; -#if MAX_AFCODES != 33 +#if MAX_AFCODES != 34 #error Need to update address-family table #endif diff --git a/usr/src/cmd/varpd/Makefile b/usr/src/cmd/varpd/Makefile new file mode 100644 index 0000000000..cff0f49f9d --- /dev/null +++ b/usr/src/cmd/varpd/Makefile @@ -0,0 +1,74 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +PROG= varpd +OBJS = varpd.o +SRCS = $(OBJS:%.o=../%.c) +SVCMETHOD = svc-varpd +MANIFEST = varpd.xml +ROOTLIBVARPD = $(ROOTLIB)/varpd +ROOTLIBVARPDPROG= $(PROG:%=$(ROOTLIBVARPD)/%) + + +include ../Makefile.cmd +include ../Makefile.ctf + +ROOTMANIFESTDIR= $(ROOTSVCNETWORK) + +CLEANFILES += $(OBJS) +CPPFLAGS += -D_REENTRANT +CFLAGS += $(CCVERBOSE) +LDLIBS += -lvarpd -lumem +$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG + +# +# Our debug only umem related functions cause lint to get confused. +# +LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2 + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +.KEEP_STATE: + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: + -$(RM) $(CLEANFILES) + +lint: lint_PROG + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +check: $(CHKMANIFEST) + +clobber: clean + $(RM) $(PROG) + +install: $(PROG) $(ROOTLIBVARPDPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) + +$(ROOTLIBVARPD): + $(INS.dir) + +$(ROOTLIBVARPD)/%: % $(ROOTLIBVARPD) + $(INS.file) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/cmd/varpd/svc-varpd b/usr/src/cmd/varpd/svc-varpd new file mode 100644 index 0000000000..ace611c8de --- /dev/null +++ b/usr/src/cmd/varpd/svc-varpd @@ -0,0 +1,35 @@ +#!/usr/bin/sh +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2015 Joyent, Inc. +# + +. /lib/svc/share/smf_include.sh + +# +# For the time being, we're going to manually make sure that the +# overlay driver is loaded. We probably shouldn't do that in the long +# run, but it helps for bootstrapping +# +add_drv overlay 2>/dev/null + +# +# This should be a service property. +# +/usr/lib/varpd/varpd -i /usr/lib/varpd +if [ $? = 0 ]; then + exit $SMF_EXIT_OK +else + exit $SMF_EXIT_ERR_FATAL +fi diff --git a/usr/src/cmd/varpd/varpd.c b/usr/src/cmd/varpd/varpd.c new file mode 100644 index 0000000000..fd1dfaa5c8 --- /dev/null +++ b/usr/src/cmd/varpd/varpd.c @@ -0,0 +1,481 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2015 Joyent, Inc. + */ + +/* + * virtual arp daemon -- varpd + * + * The virtual arp daemon is the user land counterpart to the overlay driver. To + * truly understand its purpose and how it fits into things, you should read the + * overlay big theory statement in uts/common/io/overlay/overlay.c. + * + * varod's purpose it to provide a means for looking up the destination on the + * underlay network for a host on an overlay network and to also be a door + * server such that dladm(1M) via libdladm can configure and get useful status + * information. The heavy lifting is all done by libvarpd and the various lookup + * plugins. + * + * When varpd first starts up, we take of chdiring into /var/run/varpd, which is + * also where we create /var/run/varpd.door, our door server. After that we + * daemonize and only after we daemonize do we go ahead and load plugins. The + * reason that we don't load plugins before daemonizing is that they could very + * well be creating threads and thus lose them all. In general, we want to make + * things easier on our children and not require them to be fork safe. + * + * Once it's spun up, the main varpd thread sits in sigsuspend and really just + * hangs out waiting for something, libvarpd handles everything else. + */ + +#include <libvarpd.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdlib.h> +#include <paths.h> +#include <limits.h> +#include <sys/corectl.h> +#include <signal.h> +#include <strings.h> +#include <sys/wait.h> +#include <unistd.h> +#include <thread.h> +#include <priv.h> + +#define VARPD_EXIT_REQUESTED 0 +#define VARPD_EXIT_FATAL 1 +#define VARPD_EXIT_USAGE 2 + +#define VARPD_RUNDIR "/var/run/varpd" +#define VARPD_DEFAULT_DOOR "/var/run/varpd/varpd.door" + +static varpd_handle_t *varpd_handle; +static const char *varpd_pname; +static volatile boolean_t varpd_exit = B_FALSE; + +/* + * Debug builds are automatically wired up for umem debugging. + */ +#ifdef DEBUG +const char * +_umem_debug_init() +{ + return ("default,verbose"); +} + +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); +} +#endif /* DEBUG */ + +static void +varpd_vwarn(FILE *out, const char *fmt, va_list ap) +{ + int error = errno; + + (void) fprintf(out, "%s: ", varpd_pname); + (void) vfprintf(out, fmt, ap); + + if (fmt[strlen(fmt) - 1] != '\n') + (void) fprintf(out, ": %s\n", strerror(error)); +} + +static void +varpd_fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + varpd_vwarn(stderr, fmt, ap); + va_end(ap); + + exit(VARPD_EXIT_FATAL); +} + +static void +varpd_dfatal(int dfd, const char *fmt, ...) +{ + int status = VARPD_EXIT_FATAL; + va_list ap; + + va_start(ap, fmt); + varpd_vwarn(stdout, fmt, ap); + va_end(ap); + + /* Take a single shot at this */ + (void) write(dfd, &status, sizeof (status)); + exit(status); +} + +/* ARGSUSED */ +static int +varpd_plugin_walk_cb(varpd_handle_t *vph, const char *name, void *unused) +{ + (void) printf("loaded %s!\n", name); + return (0); +} + +static int +varpd_dir_setup(void) +{ + int fd; + + if (mkdir(VARPD_RUNDIR, 0700) != 0) { + if (errno != EEXIST) + varpd_fatal("failed to create %s: %s", VARPD_RUNDIR, + strerror(errno)); + } + + fd = open(VARPD_RUNDIR, O_RDONLY); + if (fd < 0) + varpd_fatal("failed to open %s: %s", VARPD_RUNDIR, + strerror(errno)); + + if (fchown(fd, UID_NETADM, GID_NETADM) != 0) + varpd_fatal("failed to chown %s: %s\n", VARPD_RUNDIR, + strerror(errno)); + + return (fd); +} + +/* + * Because varpd is generally run under SMF, we opt to keep its stdout and + * stderr to be whatever our parent set them up to be. + */ +static void +varpd_fd_setup(void) +{ + int dupfd; + + closefrom(STDERR_FILENO + 1); + dupfd = open(_PATH_DEVNULL, O_RDONLY); + if (dupfd < 0) + varpd_fatal("failed to open %s: %s", _PATH_DEVNULL, + strerror(errno)); + if (dup2(dupfd, STDIN_FILENO) == -1) + varpd_fatal("failed to dup out stdin: %s", strerror(errno)); +} + +/* + * We borrow fmd's daemonization style. Basically, the parent waits for the + * child to successfully set up a door and recover all of the old configurations + * before we say that we're good to go. + */ +static int +varpd_daemonize(int dirfd) +{ + char path[PATH_MAX]; + struct rlimit rlim; + sigset_t set, oset; + int estatus, pfds[2]; + pid_t child; + priv_set_t *pset; + + /* + * Set a per-process core path to be inside of /var/run/varpd. Make sure + * that we aren't limited in our dump size. + */ + (void) snprintf(path, sizeof (path), + "/var/run/varpd/core.%s.%%p", varpd_pname); + (void) core_set_process_path(path, strlen(path) + 1, getpid()); + + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + (void) setrlimit(RLIMIT_CORE, &rlim); + + /* + * Claim as many file descriptors as the system will let us. + */ + if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { + rlim.rlim_cur = rlim.rlim_max; + (void) setrlimit(RLIMIT_NOFILE, &rlim); + } + + /* + * chdir /var/run/varpd + */ + if (fchdir(dirfd) != 0) + varpd_fatal("failed to chdir to %s", VARPD_RUNDIR); + + + /* + * At this point block all signals going in so we don't have the parent + * mistakingly exit when the child is running, but never block SIGABRT. + */ + if (sigfillset(&set) != 0) + abort(); + if (sigdelset(&set, SIGABRT) != 0) + abort(); + if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) + abort(); + + /* + * Do the fork+setsid dance. + */ + if (pipe(pfds) != 0) + varpd_fatal("failed to create pipe for daemonizing"); + + if ((child = fork()) == -1) + varpd_fatal("failed to fork for daemonizing"); + + if (child != 0) { + /* We'll be exiting shortly, so allow for silent failure */ + (void) close(pfds[1]); + if (read(pfds[0], &estatus, sizeof (estatus)) == + sizeof (estatus)) + _exit(estatus); + + if (waitpid(child, &estatus, 0) == child && WIFEXITED(estatus)) + _exit(WEXITSTATUS(estatus)); + + _exit(VARPD_EXIT_FATAL); + } + + /* + * Drop privileges here. + * + * We should make sure we keep around PRIV_NET_PRIVADDR and + * PRIV_SYS_DLCONFIG, but drop everything else; however, keep basic + * privs and have our child drop them. + * + * We should also run as netadm:netadm and drop all of our groups. + */ + if (setgroups(0, NULL) != 0) + abort(); + if (setgid(GID_NETADM) == -1 || seteuid(UID_NETADM) == -1) + abort(); + if ((pset = priv_allocset()) == NULL) + abort(); + priv_basicset(pset); + if (priv_delset(pset, PRIV_PROC_EXEC) == -1 || + priv_delset(pset, PRIV_PROC_INFO) == -1 || + priv_delset(pset, PRIV_PROC_FORK) == -1 || + priv_delset(pset, PRIV_PROC_SESSION) == -1 || + priv_delset(pset, PRIV_FILE_LINK_ANY) == -1 || + priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1 || + priv_addset(pset, PRIV_NET_PRIVADDR) == -1) { + abort(); + } + /* + * Remove privs from the permitted set. That will cause them to be + * removed from the effective set. We want to make sure that in the case + * of a vulnerability, something can't get back in here and wreak more + * havoc. But if we want non-basic privs in the effective set, we have + * to request them explicitly. + */ + if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) == -1) + abort(); + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) == -1) + abort(); + + priv_freeset(pset); + + if (close(pfds[0]) != 0) + abort(); + if (setsid() == -1) + abort(); + if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) + abort(); + (void) umask(0022); + + return (pfds[1]); +} + +static int +varpd_setup_lookup_threads(void) +{ + int ret; + long i, ncpus = sysconf(_SC_NPROCESSORS_ONLN) * 2 + 1; + + if (ncpus <= 0) + abort(); + for (i = 0; i < ncpus; i++) { + thread_t thr; + + ret = thr_create(NULL, 0, + (void *(*)(void *))libvarpd_overlay_lookup_run, + varpd_handle, THR_DETACHED | THR_DAEMON, &thr); + if (ret != 0) + return (ret); + } + + return (0); +} + +static void +varpd_cleanup(void) +{ + varpd_exit = B_TRUE; +} + +/* + * There are a bunch of things we need to do to be a proper daemon here. + * + * o Ensure that /var/run/varpd exists or create it + * o make stdin /dev/null (stdout?) + * o Ensure any other fds that we somehow inherited are closed, eg. + * closefrom() + * o Properly daemonize + * o Mask all signals except sigabrt before creating our first door -- all + * other doors will inherit from that. + * o Have the main thread sigsuspend looking for most things that are + * actionable... + */ +int +main(int argc, char *argv[]) +{ + int err, c, dirfd, dfd, i; + const char *doorpath = VARPD_DEFAULT_DOOR; + sigset_t set; + struct sigaction act; + int nincpath = 0, nextincpath = 0; + char **incpath = NULL; + + varpd_pname = basename(argv[0]); + + /* + * We want to clean up our file descriptors before we do anything else + * as we can't assume that libvarpd won't open file descriptors, etc. + */ + varpd_fd_setup(); + + if ((err = libvarpd_create(&varpd_handle)) != 0) { + varpd_fatal("failed to open a libvarpd handle"); + return (1); + } + + while ((c = getopt(argc, argv, ":i:d:")) != -1) { + switch (c) { + case 'i': + if (nextincpath == nincpath) { + if (nincpath == 0) + nincpath = 16; + else + nincpath *= 2; + incpath = realloc(incpath, sizeof (char *) * + nincpath); + if (incpath == NULL) { + (void) fprintf(stderr, "failed to " + "allocate memory for the %dth " + "-I option: %s\n", nextincpath + 1, + strerror(errno)); + } + + } + incpath[nextincpath] = optarg; + nextincpath++; + break; + case 'd': + doorpath = optarg; + break; + default: + (void) fprintf(stderr, "unknown option: %c\n", c); + return (1); + } + } + + dirfd = varpd_dir_setup(); + + (void) libvarpd_plugin_walk(varpd_handle, varpd_plugin_walk_cb, NULL); + + dfd = varpd_daemonize(dirfd); + + /* + * Now that we're in the child, go ahead and load all of our plug-ins. + * We do this, in part, because these plug-ins may need threads of their + * own and fork won't preserve those and we'd rather the plug-ins don't + * have to learn about fork-handlers. + */ + for (i = 0; i < nextincpath; i++) { + err = libvarpd_plugin_load(varpd_handle, incpath[i]); + if (err != 0) { + (void) fprintf(stderr, "failed to load from %s: %s\n", + optarg, strerror(err)); + return (1); + } + } + + if ((err = libvarpd_persist_enable(varpd_handle, VARPD_RUNDIR)) != 0) + varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n", + strerror(err)); + + if ((err = libvarpd_persist_restore(varpd_handle)) != 0) + varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n", + strerror(err)); + + /* + * The ur-door thread will inherit from this signal mask. So set it to + * what we want before doing anything else. In addition, so will our + * threads that handle varpd lookups. + */ + if (sigfillset(&set) != 0) + varpd_dfatal(dfd, "failed to fill a signal set..."); + + if (sigdelset(&set, SIGABRT) != 0) + varpd_dfatal(dfd, "failed to unmask SIGABRT"); + + if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) + varpd_dfatal(dfd, "failed to set our door signal mask"); + + if ((err = varpd_setup_lookup_threads()) != 0) + varpd_dfatal(dfd, "failed to create lookup threads: %s\n", + strerror(err)); + + if ((err = libvarpd_door_server_create(varpd_handle, doorpath)) != 0) + varpd_dfatal(dfd, "failed to create door server at %s: %s\n", + doorpath, strerror(err)); + + /* + * At this point, finish up signal intialization and finally go ahead, + * notify the parent that we're okay, and enter the sigsuspend loop. + */ + bzero(&act, sizeof (struct sigaction)); + act.sa_handler = varpd_cleanup; + if (sigfillset(&act.sa_mask) != 0) + varpd_dfatal(dfd, "failed to fill sigaction mask"); + act.sa_flags = 0; + if (sigaction(SIGHUP, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register HUP handler"); + if (sigaction(SIGQUIT, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register QUIT handler"); + if (sigaction(SIGINT, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register INT handler"); + if (sigaction(SIGTERM, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register TERM handler"); + + err = 0; + (void) write(dfd, &err, sizeof (err)); + (void) close(dfd); + + for (;;) { + if (sigsuspend(&set) == -1) + if (errno == EFAULT) + abort(); + if (varpd_exit == B_TRUE) + break; + } + + libvarpd_door_server_destroy(varpd_handle); + libvarpd_destroy(varpd_handle); + + return (VARPD_EXIT_REQUESTED); +} diff --git a/usr/src/cmd/varpd/varpd.xml b/usr/src/cmd/varpd/varpd.xml new file mode 100644 index 0000000000..7b01ab7253 --- /dev/null +++ b/usr/src/cmd/varpd/varpd.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- +This file and its contents are supplied under the terms of the +Common Development and Distribution License ("CDDL"), version 1.0. +You may only use this file in accordance with the terms of version +1.0 of the CDDL. + +A full copy of the text of the CDDL should have accompanied this +source. A copy of the CDDL is also available via the Internet at +http://www.illumos.org/license/CDDL. + +Copyright 2015, Joyent, Inc. +--> + +<service_bundle type="manifest" name="illumos:varpd" > + + <service name="network/varpd" type="service" version="1" > + + <create_default_instance enabled="true" /> + + <single_instance/> + + <dependency name="varpd-network-physical" + grouping="require_all" + restart_on="none" + type="service"> + <service_fmri value="svc:/network/physical:default" /> + </dependency> + + <exec_method + type="method" + name="start" + exec="/lib/svc/method/svc-varpd" + timeout_seconds="60" /> + + <exec_method + type="method" + name="stop" + exec=":kill" + timeout_seconds="10" /> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang="C">virutal ARP daemon + </loctext> + </common_name> + </template> + </service> +</service_bundle> diff --git a/usr/src/cmd/vi/port/Makefile b/usr/src/cmd/vi/port/Makefile index 268dd752a6..8e223d98e8 100644 --- a/usr/src/cmd/vi/port/Makefile +++ b/usr/src/cmd/vi/port/Makefile @@ -75,6 +75,9 @@ $(XPG6) := CFLAGS += -DXPG4 -DXPG6 -I$(SRC)/lib/libc/inc CPPFLAGS += -DUSG -DSTDIO -DVMUNIX -DTABS=8 -DSINGLE -DTAG_STACK +# vi intentionally uses foo[-1] as a sentinal value to q*column() +$(__GNUC4)CERRWARN += -_gcc=-Wno-array-bounds + # vi maintains its own versions of various routines from libc and libcurses, # so localize all symbols to avoid name space collisions. LDFLAGS += $(MAPFILE.NGB:%=-M%) diff --git a/usr/src/cmd/vi/port/ex_cmdsub.c b/usr/src/cmd/vi/port/ex_cmdsub.c index 0260d334fe..00bdcefccb 100644 --- a/usr/src/cmd/vi/port/ex_cmdsub.c +++ b/usr/src/cmd/vi/port/ex_cmdsub.c @@ -1735,7 +1735,7 @@ char *prompt; /* In ex mode, let the system hassle with setting no echo */ if (!inopen) - return (unsigned char *)getpass(prompt); + return (unsigned char *)getpass((const char *)prompt); viprintf("%s", prompt); flush(); for (p=pbuf; (c = getkey())!='\n' && c!=EOF && c!='\r';) { if (p < &pbuf[8]) diff --git a/usr/src/cmd/vndadm/Makefile b/usr/src/cmd/vndadm/Makefile new file mode 100644 index 0000000000..aa9c22d296 --- /dev/null +++ b/usr/src/cmd/vndadm/Makefile @@ -0,0 +1,65 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +PROG= vndadm +OBJS = vndadm.o +SRCS = $(OBJS:%.o=../%.c) + + +include ../Makefile.cmd +include ../Makefile.ctf + +CLEANFILES += $(OBJS) +CFLAGS += $(CCVERBOSE) +LDLIBS += -lvnd +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +all := TARGET += all +clean := TARGET += clean +clobber := TARGET += clobber +install := TARGET += install +lint := TARGET += lint + +SUBDIRS = test + +.KEEP_STATE: + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: $(SUBDIRS) + -$(RM) $(CLEANFILES) + +lint: lint_PROG $(SUBDIRS) + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +clobber: clean $(SUBDIRS) + $(RM) $(PROG) + +install: $(PROG) $(ROOTUSRSBINPROG) $(SUBDIRS) + + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/Makefile b/usr/src/cmd/vndadm/test/Makefile new file mode 100644 index 0000000000..12ef2c3a3c --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +SUBDIRS = scripts tst + +include Makefile.subdirs +include Makefile.com diff --git a/usr/src/cmd/vndadm/test/Makefile.com b/usr/src/cmd/vndadm/test/Makefile.com new file mode 100644 index 0000000000..cb096952ca --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile.com @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master +include $(SRC)/cmd/Makefile.cmd + +# +# Force c99 for everything +# +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +# +# Deal with odd lint bits. +# +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 + +# +# Install related definitions +# +ROOTOPTPKG = $(ROOT)/opt/vndtest +ROOTBIN = $(ROOTOPTPKG)/bin +ROOTTST = $(ROOTOPTPKG)/tst +ROOTTSTDIR = $(ROOTTST)/$(TSTDIR) +ROOTTSTEXES = $(EXETESTS:%=$(ROOTTSTDIR)/%) +ROOTTSTSH = $(SHTESTS:%=$(ROOTTSTDIR)/%) +ROOTOUT = $(OUTFILES:%=$(ROOTTSTDIR)/%) +ROOTTESTS = $(ROOTTSTEXES) $(ROOTTSTSH) $(ROOTOUT) +FILEMODE = 0555 +LDLIBS = $(LDLIBS.cmd) +LINTEXE = $(EXETESTS:%.exe=%.exe.ln) diff --git a/usr/src/cmd/vndadm/test/Makefile.subdirs b/usr/src/cmd/vndadm/test/Makefile.subdirs new file mode 100644 index 0000000000..957448c23b --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile.subdirs @@ -0,0 +1,29 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +.KEEP_STATE: + +all := TARGET += all +clean := TARGET += clean +clobber := TARGET += clobber +install := TARGET += install +lint := TARGET += lint + +all clean clobber install lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/cmd/vndadm/test/Makefile.targ b/usr/src/cmd/vndadm/test/Makefile.targ new file mode 100644 index 0000000000..bcbd3c8f35 --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile.targ @@ -0,0 +1,59 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +$(ROOTOPTPKG): + $(INS.dir) + +$(ROOTBIN): $(ROOTOPTPKG) + $(INS.dir) + +$(ROOTBIN)/%: %.ksh $(ROOTBIN) + $(INS.rename) + +$(ROOTTST): $(ROOTOPTPKG) + $(INS.dir) + +$(ROOTTSTDIR): $(ROOTTST) + $(INS.dir) + +$(ROOTTSTDIR)/%.ksh: %.ksh $(ROOTTSTDIR) + $(INS.file) + +$(ROOTTSTDIR)/%.out: %.out $(ROOTTSTDIR) + $(INS.file) + +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.exe: %.o $(SUPOBJS) + $(LINK.c) -o $@ $< $(SUPOBJS) $(LDLIBS) + $(POST_PROCESS) + +$(ROOTTSTDIR)/%.exe: %.exe $(ROOTTSTDIR) + $(INS.file) + +all: install + +%.exe.ln: %.c $(SUPOBJS) + $(LINT.c) $< $(LDLIBS) + +lint: $(LINTEXE) + +clean: + -$(RM) *.o $(CLEANFILES) + +clobber: clean + -$(RM) $(CLOBBERFILES) diff --git a/usr/src/cmd/vndadm/test/scripts/Makefile b/usr/src/cmd/vndadm/test/scripts/Makefile new file mode 100644 index 0000000000..d0f58918f9 --- /dev/null +++ b/usr/src/cmd/vndadm/test/scripts/Makefile @@ -0,0 +1,28 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +SRCS = vndtest +SCRIPTS = $(SRCS:%=$(ROOTBIN)/%) + +SCRIPTS := FILEMODE = 0555 +CLOBBERFILES = $(SCRIPTS) + +install: $(SCRIPTS) + +lint: + +include ../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/scripts/vndtest.ksh b/usr/src/cmd/vndadm/test/scripts/vndtest.ksh new file mode 100755 index 0000000000..1167a64802 --- /dev/null +++ b/usr/src/cmd/vndadm/test/scripts/vndtest.ksh @@ -0,0 +1,300 @@ +#!/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014, Joyent, Inc. +# + +# +# vnd test suite driver +# +unalias -a + +vt_arg0=$(basename $0) +vt_root="$(dirname $0)/.." +vt_ksh="/usr/bin/ksh" +vt_outdir= +vt_keep= +vt_all= +vt_tests= +vt_stub= +vt_vnics="vndtest1 vndtest2 vndtest3 vndtest4 vndtest5" +vt_tnum=0 +vt_tfail=0 +vt_tsuc=0 + +function usage +{ + typeset msg="$*" + [[ -z "$msg" ]] || echo "$msg" 2>&1 + cat <<USAGE >&2 +Usage: $vt_arg0 [ -o dir ] [ -k ] [ -a | test ... ] + + -o dir Sets 'dir' as the output directory + -a Runs all tests, ignores tests passed in + -k Keep output from all tests, not just failures + -m mdb binary to test +USAGE + exit 2 +} + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} + +function setup_outdir +{ + vt_outdir="$vt_outdir/$vt_arg0.$$" + mkdir -p $vt_outdir || fatal "failed to make output dir $vt_outdir" +} + +function setup_etherstub +{ + vt_ether="vndstub$$" + + dladm create-etherstub -t $vt_ether || \ + fatal "failed to create etherstub" +} + +function cleanup_vnd +{ + typeset over=$1 + typeset vnddevs vn + + vnddevs=$(vndadm list -p -d: -o datalink,name) + [[ $? -eq 0 ]] || fatal "failed to list vnics" + for v in $vnddevs; do + vn=$(echo $v | awk 'BEGIN{ FS=":"} + { if ($1 == targ) { print $2 } }' targ=$over) + [[ -z "$vn" ]] && continue + vndadm destroy $vn || fatal "failed to destroy $vn" + done +} + +function create_vnics +{ + for n in $vt_vnics; do + dladm create-vnic -t -l $vt_ether $n || fatal \ + "failed to create vnic $n over $vt_ether" + done +} + +function cleanup_vnics +{ + typeset nics vn + + nics=$(dladm show-vnic -p -o over,link) + [[ $? -eq 0 ]] || fatal "failed to list vnics" + for n in $nics; do + vn=$(echo $n | awk 'BEGIN{ FS=":"} + { if ($1 == targ) { print $2 } }' targ=$vt_ether ) + [[ -z "$vn" ]] && continue + cleanup_vnd $vn + # + # There may or may not be an IP device on our nics... + # + ifconfig $vn down unplumb 2>/dev/null || /bin/true + dladm delete-vnic $vn || fatal "failed to delete vnic $n" + done + +} + +function cleanup_etherstub +{ + cleanup_vnics + dladm delete-etherstub -t $vt_ether || \ + fatal "failed to delete etherstub" +} + +function run_single +{ + typeset name=$1 + typeset expect base ext exe command odir res reason + typeset iserr + + [[ -z "$name" ]] && fail "missing test to run" + base=${name##*/} + ext=${base##*.} + expect=${base%%.*} + odir="$vt_outdir/current" + [[ -z "$ext" ]] && fatal "found test without ext: $name" + [[ -z "$expect" ]] && fatal "found test without prefix: $name" + + [[ "$expect" == "create" || "$expect" == "ecreate" ]] && create_vnics + if [[ "$expect" == "err" || "$expect" == "ecreate" ]]; then + iserr="yup" + else + iserr="" + fi + + case "$ext" in + "ksh") + command="$vt_ksh ./$base" + ;; + "exe") + command="./$base" + ;; + "out") + # + # This is the file format for checking output against. + # + return 0 + ;; + *) + echo "skipping test $name (unknown extensino)" + return 0 + ;; + esac + + echo "Executing test $name ... \c" + mkdir -p "$odir" >/dev/null || fatal "can't make output directory" + cd $(dirname $name) || fatal "failed to enter test directory" + $command $vt_vnics > "$odir/stdout" 2>"$odir/stderr" + res=$? + cd - > /dev/null || fatal "failed to leave test directory" + + if [[ -f "$name.out" ]] && \ + ! diff "$name.out" "$odir/stdout" >/dev/null; then + cp $name.out $odir/$base.out + reason="stdout mismatch" + elif [[ -n "$iserr" && $res -eq 0 ]]; then + reason="test exited $res, not non-zero" + elif [[ -z "$iserr" && $res -ne 0 ]]; then + reason="test exited $res, not zero" + fi + + if [[ -n "$reason" ]]; then + echo "$reason" + ((vt_tfail++)) + mv "$odir" "$vt_outdir/failure.$vt_tfail" || fatal \ + "failed to move test output directory" + cp "$name" "$vt_outdir/failure.$vt_tfail/$(basename $name)" || \ + fatal "failed to copy test into output directory" + else + echo "passed" + ((vt_tsuc++)) + mv "$odir" "$vt_outdir/success.$vt_tsuc" || fatal \ + "failed to move test directory" + fi + + [[ "$expect" == "create" || "$expect" == "ecreate" ]] && cleanup_vnics + + ((vt_tnum++)) +} + +function run_all +{ + typeset tests t dir + + cd $vt_root || fatal "failed to enter root test directory" + tests=$(ls -1 */*/@(ecreate|create|tst|err).*.@(ksh|exe)) + cd - > /dev/null + for t in $tests; do + run_single $t + done +} + +function welcome +{ + cat <<WELCOME +Starting tests... +output directory: $vt_outdir +WELCOME +} + +function cleanup +{ + [[ -n "$vt_keep" ]] && return + rm -rf "$vt_outdir"/success.* || fatal \ + "failed to remove successful test cases" + if [[ $vt_tfail -eq 0 ]]; then + rmdir "$vt_outdir" || fatal \ + "failed to remove test output directory" + fi +} + +function goodbye +{ + cat <<EOF + +------------- +Results +------------- + +Tests passed: $vt_tsuc +Tests failed: $vt_tfail +Tests ran: $vt_tnum + +EOF + if [[ $vt_tfail -eq 0 ]]; then + echo "Congrats, vnd isn't completely broken, the tests pass". + else + echo "Some tests failed, you have some work to do." + fi +} + +while getopts ":ahko:m:" c $@; do + case "$c" in + a) + vt_all="y" + ;; + k) + vt_keep="y" + ;; + o) + vt_outdir="$OPTARG" + ;; + h) + usage + ;; + :) + usage "option requires an argument -- $OPTARG" + ;; + *) + usage "invalid option -- $OPTARG" + ;; + esac +done + +shift $((OPTIND-1)) + +[[ $(zonename) != "global" ]] && fatal "vndtest only runs in the global zone" + +[[ -z "$vt_all" && $# == 0 ]] && usage "no tests to run" + +[[ -z "$vt_outdir" ]] && vt_outdir="$PWD" + +setup_outdir +setup_etherstub +welcome + +if [[ ! -z "$vt_all" ]]; then + run_all +else + for t in $@; do + [[ -f $t ]] || fatal "cannot find test $t" + run_single $t + done +fi + +cleanup_etherstub +goodbye +cleanup + +# +# Exit 1 if we have tests that return non-zero +# +[[ $vt_tfai -eq 0 ]] diff --git a/usr/src/cmd/vndadm/test/tst/Makefile b/usr/src/cmd/vndadm/test/tst/Makefile new file mode 100644 index 0000000000..9b1ba29429 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +SUBDIRS = cmd dld ioctl lib + +include ../Makefile.subdirs diff --git a/usr/src/cmd/vndadm/test/tst/cmd/Makefile b/usr/src/cmd/vndadm/test/tst/cmd/Makefile new file mode 100644 index 0000000000..1ca20bf749 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/Makefile @@ -0,0 +1,34 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = cmd +COMMONSH = cmd.common.ksh +SHTESTS = $(COMMONSH) \ + create.list.ksh \ + create.sdev.ksh \ + create.setbuf.ksh \ + ecreate.destroy.ksh \ + ecreate.setbadprop.ksh \ + ecreate.setbadvalue.ksh \ + ecreate.setbuftoobig.ksh \ + ecreate.setrdonlyprop.ksh + +OUTFILES = create.list.ksh.out + +include ../../Makefile.com + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh b/usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh new file mode 100644 index 0000000000..31e4e8bf5c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh @@ -0,0 +1,33 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Common ksh-based utilities +# + +vt_arg0=$(basename $0) + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} + +[[ -z "$1" ]] && fatal "missing required vnic" +[[ -z "$2" ]] && fatal "missing required vnic" +[[ -z "$3" ]] && fatal "missing required vnic" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh new file mode 100644 index 0000000000..fdec9a85be --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh @@ -0,0 +1,30 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Basic device listing +# + +. ./cmd.common.ksh + +# +# Use what we hope is a relatively unique name +# +cl_name="triforceofcourage0" +vndadm create -l $1 $cl_name || fatal "failed to create vnd device" +vndadm list -p -o name,zone $cl_name +vndadm list -p -d: -o zone,name $cl_name +vndadm destroy $cl_name || fatal "failed to destroy vnd device" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out new file mode 100644 index 0000000000..d208b38aab --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out @@ -0,0 +1,2 @@ +triforceofcourage0 global +global:triforceofcourage0 diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh b/usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh new file mode 100644 index 0000000000..b816ade1de --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh @@ -0,0 +1,25 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Verify that our sdev links exist +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd" +[[ -c /dev/vnd/$1 ]] || fatal "missing link" +[[ -c /dev/vnd/zone/$(zonename)/$1 ]] || fatal "missing per-zone link" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh b/usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh new file mode 100644 index 0000000000..d50edbead4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh @@ -0,0 +1,34 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Set and validate the buffer size properties. Valiate that we can set +# the value using the various number analogues, eg. 1024K, etc. +# +set -o pipefail + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 rxbuf=1M +cur=$(vndadm get -p $1 rxbuf | nawk '{ print $4 }') +[[ $? -eq 0 ]] || fatal "failed to get rxbuf" +[[ $cur -eq 1048576 ]] || fatal "rxbuf is $cur, not 1M" + +vndadm set $1 txbuf=1024K +cur=$(vndadm get -p $1 rxbuf | nawk '{ print $4 }') +[[ $? -eq 0 ]] || fatal "failed to get txbuf" +[[ $cur -eq 1048576 ]] || fatal "txbuf is $cur, not 1M" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh new file mode 100644 index 0000000000..e3c4931018 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh @@ -0,0 +1,25 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that destroy on a previously destroyed link fails +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm destroy $1 || fatal "failed to destroy vnd device" +vndadm destroy $1 diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh new file mode 100644 index 0000000000..30c27575b1 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set a non-existant proprety +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 ganon=ganondorf diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh new file mode 100644 index 0000000000..056b24a817 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set something to a garbage value +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 rxbuf=hello diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh new file mode 100644 index 0000000000..551e20461c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set a buffer value to a ridiculous size +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 rxsize=1T diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh new file mode 100644 index 0000000000..4beb53e227 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set a read only property. +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 mintu=100 diff --git a/usr/src/cmd/vndadm/test/tst/dld/Makefile b/usr/src/cmd/vndadm/test/tst/dld/Makefile new file mode 100644 index 0000000000..3088812630 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/Makefile @@ -0,0 +1,27 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = dld +COMMONSH = dld.common.ksh +SHTESTS = $(COMMONSH) \ + ecreate.ipfirst.ksh \ + ecreate.vndfirst.ksh \ + create.reuse.ksh + +include ../../Makefile.com + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh b/usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh new file mode 100644 index 0000000000..bc2ffde7f6 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh @@ -0,0 +1,31 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can reuse a data link +# + +. ./dld.common.ksh + +dld_nic=$1 +[[ -z "$1" ]] && fatal "missing required vnic" + +vndadm create $dld_nic || fatal "failed to bring up vnd" +vndadm destroy $dld_nic || fatal "failed to bring down vnd" +ifconfig $dld_nic plumb up || fatal "failed to bring up IP" +ifconfig $dld_nic down unplumb || fatal "failed to bring down IP" +vndadm create $dld_nic || fatal "failed to bring up vnd" +vndadm destroy $dld_nic || fatal "failed to bring down vnd" diff --git a/usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh b/usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh new file mode 100644 index 0000000000..7a2e0a8e2b --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh @@ -0,0 +1,29 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Common ksh-based utilities +# + +vt_arg0=$(basename $0) + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} diff --git a/usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh b/usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh new file mode 100644 index 0000000000..e6409781cb --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh @@ -0,0 +1,27 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure vnd fails to come up when IP is up +# + +. ./dld.common.ksh + +dld_nic=$1 +[[ -z "$1" ]] && fatal "missing required vnic" + +ifconfig $dld_nic plumb up || fatal "failed to bring up IP" +vndadm create $dld_nic diff --git a/usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh b/usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh new file mode 100644 index 0000000000..ee7a13c09c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh @@ -0,0 +1,27 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure IP fails to come up when vnd is up +# + +. ./dld.common.ksh + +dld_nic=$1 +[[ -z "$1" ]] && fatal "missing required vnic" + +vndadm create $dld_nic || fatal "failed to bring up vnd" +ifconfig $dld_nic plumb up diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/Makefile b/usr/src/cmd/vndadm/test/tst/ioctl/Makefile new file mode 100644 index 0000000000..fe074f32b0 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/Makefile @@ -0,0 +1,49 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = ioctl +EXETESTS = \ + create.attach.exe \ + create.attachnolink.exe \ + create.badlinkname.exe \ + create.doublelink.exe \ + create.gioctlattach.exe \ + create.link.exe \ + create.linkexists.exe \ + create.ngioctlfault.exe \ + create.nopriv1.exe \ + create.nopriv2.exe \ + create.nopriv3.exe \ + create.nopriv4.exe \ + create.olink.exe \ + create.olinknopriv.exe \ + create.rmenolink.exe \ + tst.attachrdonly.exe \ + tst.basicopenctl.exe \ + tst.badioctl.exe \ + tst.gioctlfault.exe \ + tst.gioctlnattach.exe \ + tst.openctlbadflags.exe +SHTESTS = \ + tst.iocsize.ksh +SUPBOBJS = + +CLOBBERFILES = $(EXETESTS) + +include ../../Makefile.com + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c new file mode 100644 index 0000000000..d7bca5cce3 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Simply attach a nic + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c new file mode 100644 index 0000000000..43c6c99af5 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c @@ -0,0 +1,67 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Try to attach to a non-existant vnic + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + /* + * All datalink names have numbers, so we can pick a datalink which + * doesn't exist by not using numbers... + */ + (void) strlcpy(via.via_name, "enolink", VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(via.via_errno == VND_E_NODATALINK); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c new file mode 100644 index 0000000000..e3a067d5ce --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c @@ -0,0 +1,119 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Test that we can't link a nic with invalid names + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +static const char *names[] = { + /* Reserved names */ + "ctl", + "zone", + /* Invalid characters */ + "The fight of the century", + "Link/Ganon", + "happens@7pm", + "#testing", + "asdf!!", + "power&courage&wisdom", + "over9000?", + "you're", + "100$", + "(function", + "x)", + "2^128", + "1++", + "No.", + "99%", + "*****", + "r|m", + "=0", + "`p0", + "goodbye~", + "however;", + "\"hesaid", + "shesaid\'", + /* emoji pile of poo */ + "\xF0\x9F\x92\xA9", + NULL +}; + +int +main(int argc, const char *argv[]) +{ + int fd, ret, i; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + for (i = 0; names[i] != NULL; i++) { + (void) strlcpy(vil.vil_name, names[i], VND_NAMELEN); + (void) fprintf(stderr, "Trying to create [%s]\n", names[i]); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_BADNAME); + } + + /* Finally, the missing null terminator */ + for (i = 0; i < VND_NAMELEN; i++) + vil.vil_name[i] = 'a'; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_BADNAME); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == -1); + assert(viu.viu_errno == VND_E_NOTLINKED); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c new file mode 100644 index 0000000000..dcf4f311e9 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c @@ -0,0 +1,82 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Link a nic, first should work, second will fail. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + (void) strlcpy(vil.vil_name, "dup", VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_LINKED); + viu.viu_errno = 0; + + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c new file mode 100644 index 0000000000..3d6f43377b --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Ensure that we can't run global ioctls on an attached handle + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + via.via_name[0] = 'a'; + via.via_name[1] = '\0'; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(via.via_errno == VND_E_ATTACHED); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.link.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.link.c new file mode 100644 index 0000000000..16569d58cd --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.link.c @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Link a nic + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c new file mode 100644 index 0000000000..4e3be0db5d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c @@ -0,0 +1,90 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Try to create two devices with the same link name. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, fd2, ret; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 3) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + fd2 = open(VND_PATH, O_RDWR); + assert(fd2 > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(via.via_name, argv[2], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd2, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, "dup", VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + (void) strlcpy(vil.vil_name, "dup", VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd2, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_LINKEXISTS); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c new file mode 100644 index 0000000000..bf174f1a8f --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c @@ -0,0 +1,96 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Pass bad addresses to all of our non-global ioctls + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +static int requests[] = { + VND_IOC_LINK, + VND_IOC_UNLINK, + VND_IOC_GETRXBUF, + VND_IOC_SETRXBUF, + VND_IOC_GETTXBUF, + VND_IOC_SETTXBUF, + VND_IOC_GETMINTU, + VND_IOC_GETMAXTU, + VND_IOC_GETMAXBUF, + -1 +}; + +int +main(int argc, const char *argv[]) +{ + int fd, ret, i; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + for (i = 0; requests[i] != -1; i++) { + ret = ioctl(fd, requests[i], (void *)(uintptr_t)i); + assert(ret == -1); + assert(errno == EFAULT); + } + + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c new file mode 100644 index 0000000000..6d5ad0eec2 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach a device without PRIV_NET_CONFIG + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <string.h> +#include <unistd.h> +#include <stropts.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_CONFIG) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c new file mode 100644 index 0000000000..6b38f159a0 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach a device without PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c new file mode 100644 index 0000000000..a8c43fc46d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c @@ -0,0 +1,70 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach a device without PRIV_NET_CONFIG and PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_CONFIG) == 0); + assert(priv_addset(ps, PRIV_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c new file mode 100644 index 0000000000..aed0204544 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c @@ -0,0 +1,75 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to link a device without PRIV_NET_CONFIG + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_CONFIG) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c new file mode 100644 index 0000000000..2db8ecc95f --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c @@ -0,0 +1,77 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to open a device without PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, fd2, ret; + priv_set_t *ps; + char *path; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + (void) asprintf(&path, "/dev/vnd/%s", argv[1]); + assert(path != NULL); + fd2 = open(path, O_RDWR); + assert(fd2 == -1); + assert(errno == EPERM); + + free(path); + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c new file mode 100644 index 0000000000..0f9292bbae --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c @@ -0,0 +1,77 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Open a /dev/vnd/%s link + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + char *path; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + ret = asprintf(&path, "/dev/vnd/%s", argv[1]); + assert(ret != -1); + + ret = open(path, O_RDONLY); + assert(ret > 0); + assert(close(ret) == 0); + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c new file mode 100644 index 0000000000..338218e751 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c @@ -0,0 +1,83 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to open a /dev/vnd/%s without PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> +#include <priv.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + char *path; + priv_set_t *ps; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + ret = asprintf(&path, "/dev/vnd/%s", argv[1]); + assert(ret != -1); + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + ret = open(path, O_RDWR); + assert(ret == -1); + assert(errno == EPERM); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c new file mode 100644 index 0000000000..d44e6512a7 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that unlink fails when we're not linked. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == -1); + assert(viu.viu_errno == VND_E_NOTLINKED); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c new file mode 100644 index 0000000000..29def6182d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach when /dev/vnd/ctl is opened read only. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDONLY); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EBADF); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c new file mode 100644 index 0000000000..f26722f035 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c @@ -0,0 +1,79 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Throw a bunch of bad ioctls at us and make sure that we get ENOTTY. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <assert.h> + +/* + * We're including a bunch of bad header files that have ioctl numbers that we + * know we shouldn't. + */ +#include <sys/ipd.h> +#include <sys/dtrace.h> + +#define VND_PATH "/dev/vnd/ctl" + +/* + * A series of bad requests + */ +static int requests[] = { + 0, + 1, + 42, + 169, + 4096, + INT_MAX, + IPDIOC_CORRUPT, + IPDIOC_REMOVE, + DTRACEIOC_CONF, + DTRACEIOC_REPLICATE, + -1 +}; + +int +main(void) +{ + int fd, i; + + fd = open(VND_PATH, O_RDONLY); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s read only: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + for (i = 0; requests[i] != -1; i++) { + int ret; + ret = ioctl(fd, requests[i], NULL); + assert(ret == -1); + assert(errno == ENOTTY); + } + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c new file mode 100644 index 0000000000..852ad5550f --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Ensure that we can do a basic open of the device for read, write, and + * read/write. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(void) +{ + int fd; + + fd = open(VND_PATH, O_RDONLY); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s read only: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + if (close(fd) != 0) { + (void) fprintf(stderr, "failed to close vnd fd: %s\n", + strerror(errno)); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s read/write: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + if (close(fd) != 0) { + (void) fprintf(stderr, "failed to close vnd fd: %s\n", + strerror(errno)); + return (1); + } + + fd = open(VND_PATH, O_WRONLY); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s write only: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + if (close(fd) != 0) { + (void) fprintf(stderr, "failed to close vnd fd: %s\n", + strerror(errno)); + return (1); + } + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c new file mode 100644 index 0000000000..b581b5dd4c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c @@ -0,0 +1,78 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Pass pointers to arbitrary addresses and make sure we properly get EFAULT for + * all the global ioctls. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <assert.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(void) +{ + int fd, ret; + vnd_ioc_attach_t *via; + vnd_ioc_list_t *vil; + vnd_ioc_buf_t *vib; + + fd = open(VND_PATH, O_RDWR); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s r/w: %s\n", VND_PATH, + strerror(errno)); + return (1); + } + + via = (vnd_ioc_attach_t *)(uintptr_t)23; + vil = (vnd_ioc_list_t *)(uintptr_t)42; + vib = (vnd_ioc_buf_t *)(uintptr_t)169; + + ret = ioctl(fd, VND_IOC_ATTACH, NULL); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_LIST, NULL); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_GETMAXBUF, NULL); + assert(ret == -1); + assert(errno == EFAULT); + + ret = ioctl(fd, VND_IOC_ATTACH, via); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_LIST, vil); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_GETMAXBUF, vib); + assert(ret == -1); + assert(errno == EFAULT); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c new file mode 100644 index 0000000000..98acffa194 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c @@ -0,0 +1,100 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Here we test that all the ioctls which require us to be on a local device + * fail to work. Specifically, the errno should be VND_E_NOTATTACHED + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <assert.h> +#include <stdlib.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +static int vib_ioc[] = { + VND_IOC_GETRXBUF, + VND_IOC_SETRXBUF, + VND_IOC_GETTXBUF, + VND_IOC_SETTXBUF, + VND_IOC_GETMINTU, + VND_IOC_GETMAXTU, + -1 +}; + +int +main(void) +{ + int fd, ret, i; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + vnd_ioc_buf_t vib; + frameio_t *fio; + char buf[1]; + + fd = open(VND_PATH, O_RDWR); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s r/w: %s\n", VND_PATH, + strerror(errno)); + return (1); + } + + bzero(&vil, sizeof (vnd_ioc_link_t)); + vil.vil_name[0] = 'a'; + bzero(&viu, sizeof (vnd_ioc_unlink_t)); + bzero(&vib, sizeof (vnd_ioc_buf_t)); + fio = malloc(sizeof (frameio_t) + sizeof (framevec_t)); + assert(fio != NULL); + fio->fio_version = FRAMEIO_CURRENT_VERSION; + fio->fio_nvpf = 1; + fio->fio_nvecs = 1; + fio->fio_vecs[0].fv_buf = buf; + fio->fio_vecs[0].fv_buflen = 1; + + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(vil.vil_errno == VND_E_NOTATTACHED); + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(viu.viu_errno == VND_E_NOTLINKED); + + for (i = 0; vib_ioc[i] != -1; i++) { + bzero(&vib, sizeof (vib)); + ret = ioctl(fd, vib_ioc[i], &vib); + assert(vib.vib_errno == VND_E_NOTATTACHED); + } + + /* The frameio ioctls only use standard errnos */ + ret = ioctl(fd, VND_IOC_FRAMEIO_READ, fio); + assert(ret == -1); + assert(errno == ENXIO); + ret = ioctl(fd, VND_IOC_FRAMEIO_WRITE, fio); + assert(ret == -1); + assert(errno == ENXIO); + + free(fio); + assert(close(fd) == 0); + + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh b/usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh new file mode 100644 index 0000000000..9b30043d47 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh @@ -0,0 +1,54 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Ensure structure sizes for both ILP32 and LP64 are the same +# + +vt_arg0=$(basename $0) +vt_structs="vnd_ioc_attach_t vnd_ioc_link_t vnd_ioc_unlink_t" +vt_structs="$vt_structs vnd_ioc_nonblock_t vnd_ioc_buf_t vnd_ioc_info_t" + +vt_t32="/tmp/vnd.iocsize.32.$$" +vt_t64="/tmp/vnd.iocsize.64.$$" + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} + +function dump_types +{ + typeset file=$1 + typeset lib=$2 + typeset t + + for t in $vn_structs; do + mdb -e \'::print -at $t\' $lib >> $file || fatal \ + "failed to dump type $t from $lib" + done +} + +rm -f $vt_t32 $vt_t64 || fatal "failed to cleanup old temp files" +touch $vt_t32 $vt_t64 || fatal "failed to create temp files" + +dump_types $vt_t32 /usr/lib/libvnd.so.1 +dump_types $vt_t64 /usr/lib/64/libvnd.so.1 + +diff $vt_t32 $vt_t64 diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c new file mode 100644 index 0000000000..65e48029b7 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c @@ -0,0 +1,88 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't open the vnd control device with invalid flags. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(void) +{ + int fd; + + fd = open(VND_PATH, O_RDONLY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_RDWR | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_WRONLY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_RDONLY | O_NDELAY); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY!"); + return (1); + } + + fd = open(VND_PATH, O_RDWR | O_NDELAY); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY!"); + return (1); + } + + fd = open(VND_PATH, O_WRONLY | O_NDELAY); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY!"); + return (1); + } + + fd = open(VND_PATH, O_RDONLY | O_NDELAY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY | O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_RDWR | O_NDELAY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY | O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_WRONLY | O_NDELAY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY | O_EXCL!"); + return (1); + } + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/Makefile b/usr/src/cmd/vndadm/test/tst/lib/Makefile new file mode 100644 index 0000000000..d7a1ed8fa5 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/Makefile @@ -0,0 +1,44 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = lib +EXETESTS = \ + create.basic.exe \ + create.badlink.exe \ + create.badpropid.exe \ + create.badpropsize.exe \ + create.badzone.exe \ + create.enomem.exe \ + create.frameioeagain.exe \ + create.open.exe \ + create.propiter.exe \ + create.proprdonly.exe \ + err.badclose.exe \ + tst.badopen.exe \ + tst.strerror.exe \ + tst.strsyserror.exe +OUTFILES = tst.strerror.exe.out +SHTESTS = +SUPBOBJS = + +CLOBBERFILES = $(EXETESTS) + +include ../../Makefile.com + +LDLIBS += -lvnd + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badlink.c b/usr/src/cmd/vndadm/test/tst/lib/create.badlink.c new file mode 100644 index 0000000000..aefec3ed44 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badlink.c @@ -0,0 +1,39 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't create something in the context of a datalink that + * doesn't exist. + */ + +#include <assert.h> +#include <stdio.h> +#include <libvnd.h> + +int +main(void) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + vhp = vnd_create(NULL, "foobar", "foobar", &vnderr, &syserr); + (void) printf("%d, %d\n", vnderr, syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_NODATALINK); + assert(syserr == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c b/usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c new file mode 100644 index 0000000000..15334fa31c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't get and set nonexisting properties. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + ret = vnd_prop_get(vhp, VND_PROP_MAX, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_get(vhp, VND_PROP_MAX + 5, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_set(vhp, VND_PROP_MAX, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_set(vhp, VND_PROP_MAX + 5, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_writeable(VND_PROP_MAX, NULL); + assert(ret == -1); + + ret = vnd_prop_writeable(VND_PROP_MAX + 5, NULL); + assert(ret == -1); + + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c b/usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c new file mode 100644 index 0000000000..d5fefd3764 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Validate that we can't set properties with bogus sizes. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <limits.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret, i; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + for (i = 0; i < VND_PROP_MAX; i++) { + ret = vnd_prop_get(vhp, i, NULL, INT32_MAX); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROPSIZE); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_set(vhp, i, NULL, INT32_MAX); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROPSIZE); + assert(vnd_syserrno(vhp) == 0); + } + + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badzone.c b/usr/src/cmd/vndadm/test/tst/lib/create.badzone.c new file mode 100644 index 0000000000..30f9612963 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badzone.c @@ -0,0 +1,43 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't create something in the context of a zone that + * doesn't exist. + */ + +#include <assert.h> +#include <sys/zone.h> +#include <string.h> +#include <libvnd.h> + +int +main(void) +{ + int syserr; + vnd_errno_t vnderr; + char zname[ZONENAME_MAX+4]; + vnd_handle_t *vhp; + + (void) memset(zname, 'a', sizeof (zname)); + zname[ZONENAME_MAX+3] = '\0'; + + vhp = vnd_create(zname, "foobar", "foobar", &vnderr, &syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_NOZONE); + assert(syserr == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.basic.c b/usr/src/cmd/vndadm/test/tst/lib/create.basic.c new file mode 100644 index 0000000000..5335f8cbb4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.basic.c @@ -0,0 +1,49 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Simple create and destroy. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.enomem.c b/usr/src/cmd/vndadm/test/tst/lib/create.enomem.c new file mode 100644 index 0000000000..9203e369ae --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.enomem.c @@ -0,0 +1,91 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that we can't allocate a handle when in an ENOMEM situation. + */ + +#include <procfs.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/sysmacros.h> +#include <assert.h> +#include <strings.h> + +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int fd; + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + pstatus_t status; + void *addr; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open("/proc/self/status", O_RDONLY); + if (fd < 0) + exit(1); + if (read(fd, &status, sizeof (status)) != sizeof (status)) + exit(1); + + addr = mmap((caddr_t)P2ROUNDUP(status.pr_brkbase + + status.pr_brksize, 0x1000), 0x1000, + PROT_READ, MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0); + if (addr == (void *)-1) { + perror("mmap"); + exit(1); + } + + /* malloc an approximate size of the vnd_handle_t */ + for (;;) { + void *buf; + + buf = malloc(8); + if (buf == NULL) + break; + } + + for (;;) { + void *buf; + + buf = malloc(4); + if (buf == NULL) + break; + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_NOMEM); + assert(syserr == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c b/usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c new file mode 100644 index 0000000000..6cb14fb7df --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c @@ -0,0 +1,80 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Create a datalink, set it to non-blocking mode and ensure that we get EAGAIN + * from frame I/O calls. Note that if this test is not plumbed up over an + * etherstub, then it is likely that other traffic will appear on the device and + * this will fail. Note that the test suite always creates these devices over an + * etherstub. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret, fd; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + frameio_t *fio; + char buf[1520]; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + fd = vnd_pollfd(vhp); + ret = fcntl(fd, F_SETFL, O_NONBLOCK); + assert(ret == 0); + + fio = malloc(sizeof (frameio_t) + + sizeof (framevec_t)); + assert(fio != NULL); + fio->fio_version = FRAMEIO_CURRENT_VERSION; + fio->fio_nvpf = 1; + fio->fio_nvecs = 1; + + fio->fio_vecs[0].fv_buf = buf; + fio->fio_vecs[0].fv_buflen = sizeof (buf); + + ret = vnd_frameio_read(vhp, fio); + (void) printf("%d, %d\n", ret, errno); + assert(ret == -1); + assert(errno == EAGAIN); + + vnd_close(vhp); + free(fio); + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.open.c b/usr/src/cmd/vndadm/test/tst/lib/create.open.c new file mode 100644 index 0000000000..9cb1d7e40e --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.open.c @@ -0,0 +1,56 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure we can open a created datalink. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp, *vhp2; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + vhp2 = vnd_open(NULL, argv[1], &vnderr, &syserr); + assert(vhp2 != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + vnd_close(vhp2); + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.propiter.c b/usr/src/cmd/vndadm/test/tst/lib/create.propiter.c new file mode 100644 index 0000000000..a0b46180f7 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.propiter.c @@ -0,0 +1,79 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Ensure that vnd_prop_iter sees all props; + */ + +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <assert.h> +#include <libvnd.h> + +static boolean_t *g_props; + +/* ARGSUSED */ +static int +prop_cb(vnd_handle_t *vhp, vnd_prop_t prop, void *unused) +{ + assert(prop < VND_PROP_MAX); + g_props[prop] = B_TRUE; + + return (0); +} + +int +main(int argc, const char *argv[]) +{ + int syserr, i, ret; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + g_props = malloc(sizeof (boolean_t) * VND_PROP_MAX); + if (g_props == NULL) { + (void) fprintf(stderr, "failed to alloc memory for %d " + "boolean_t\n", VND_PROP_MAX); + return (1); + } + for (i = 0; i < VND_PROP_MAX; i++) + g_props[i] = B_FALSE; + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + ret = vnd_prop_iter(vhp, prop_cb, NULL); + assert(ret == 0); + + for (i = 0; i < VND_PROP_MAX; i++) + assert(g_props[i] == B_TRUE); + + free(g_props); + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c b/usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c new file mode 100644 index 0000000000..18b1f7d58d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Validate that we can't set read only properties + */ + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <limits.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + vnd_prop_buf_t vpb; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + ret = vnd_prop_get(vhp, VND_PROP_MINTU, &vpb, + sizeof (vnd_prop_buf_t)); + assert(ret == 0); + + ret = vnd_prop_set(vhp, VND_PROP_MINTU, &vpb, + sizeof (vnd_prop_buf_t)); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_PROPRDONLY); + assert(vnd_syserrno(vhp) == 0); + + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/err.badclose.c b/usr/src/cmd/vndadm/test/tst/lib/err.badclose.c new file mode 100644 index 0000000000..8c832506a0 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/err.badclose.c @@ -0,0 +1,33 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * This program should segfault. + */ + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(void) +{ + vnd_handle_t *vhp = (void *)0x42; + vnd_close(vhp); + /* This should not be reached */ + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c b/usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c new file mode 100644 index 0000000000..4f67ce79ed --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c @@ -0,0 +1,49 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure we can't open a vnd device that doesn't exist + */ + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_open(NULL, argv[1], &vnderr, &syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_SYS); + assert(syserr == ENOENT); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c new file mode 100644 index 0000000000..a99a9ecbf6 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c @@ -0,0 +1,30 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that all the error strings we care about match what we expect. + */ + +#include <stdio.h> +#include <libvnd.h> + +int +main(void) +{ + int i; + for (i = 0; i <= VND_E_UNKNOWN + 1; i++) + (void) printf("[%s]\n", vnd_strerror(i)); + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out new file mode 100644 index 0000000000..83dbcdfdb4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out @@ -0,0 +1,37 @@ +[no error] +[not enough memory available] +[no such datalink] +[datalink not of type DL_ETHER] +[unknown dlpi failure] +[DL_ATTACH_REQ failed] +[DL_BIND_REQ failed] +[DL_PROMISCON_REQ failed] +[DLD_CAPAB_DIRECT enable failed] +[bad datalink capability] +[bad datalink subcapability] +[bad dld version] +[failed to create kstats] +[no such vnd link] +[netstack doesn't exist] +[device already associated] +[device already attached] +[device already linked] +[invalid name] +[permission denied] +[no such zone] +[failed to initialize vnd stream module] +[device not attached] +[device not linked] +[another device has the same link name] +[failed to create minor node] +[requested buffer size is too large] +[requested buffer size is too small] +[unable to obtain exclusive access to dlpi link, link busy] +[DLD direct capability not supported over data link] +[invalid property size] +[invalid property] +[property is read only] +[unexpected system error] +[capabilities invalid, pass-through module detected] +[unknown error] +[unknown error] diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c b/usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c new file mode 100644 index 0000000000..b95e6372e4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c @@ -0,0 +1,50 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that the error message from libvnd's strsyserrno is the same as the + * underlying strerror function's. It should be. We'll just check an assortment + * of errnos. + */ + +#include <stdio.h> +#include <string.h> +#include <libvnd.h> + +int +main(void) +{ + int i; + const char *vnd, *libc; + for (i = 0; i < 42; i++) { + vnd = vnd_strsyserror(i); + libc = strerror(i); + if ((vnd != NULL && libc == NULL) || + (vnd == NULL && libc != NULL)) { + (void) fprintf(stderr, "errno %d, vnd: %p, libc: %p", + i, (void *)vnd, (void *)libc); + return (1); + } + if (vnd != NULL && strcmp(vnd, libc) != 0) { + (void) fprintf(stderr, + "errno %d: libc and vnd disagree.\n", i); + (void) fprintf(stderr, "vnd: %s\n", vnd); + (void) fprintf(stderr, "libc: %s\n", libc); + return (1); + } + } + + return (0); +} diff --git a/usr/src/cmd/vndadm/vndadm.c b/usr/src/cmd/vndadm/vndadm.c new file mode 100644 index 0000000000..6811663696 --- /dev/null +++ b/usr/src/cmd/vndadm/vndadm.c @@ -0,0 +1,872 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#include <errno.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include <libgen.h> +#include <stdlib.h> +#include <unistd.h> +#include <zone.h> + +#include <libvnd.h> + +typedef int (*vndadm_print_t)(vnd_handle_t *, vnd_prop_t); +typedef int (*vndadm_parse_t)(char *, void **, size_t *); + +typedef struct vndadm_proptbl { + const char *vp_name; + vndadm_print_t vp_print; + vndadm_parse_t vp_parse; +} vndadm_proptbl_t; + +/* + * Forwards + */ +static int usage(const char *, ...); +static int vndadm_print_size(vnd_handle_t *, vnd_prop_t); +static int vndadm_print_number(vnd_handle_t *, vnd_prop_t); +static int vndadm_parse_size(char *, void **, size_t *); + +/* + * Globals + */ +static char *vnd_pname; + +static void +vnd_vwarn(vnd_errno_t verr, int syserr, const char *format, va_list alist) +{ + (void) fprintf(stderr, "%s: ", vnd_pname); + (void) vfprintf(stderr, format, alist); + if (strchr(format, '\n') == NULL) { + (void) fprintf(stderr, ": %s\n", verr != VND_E_SYS ? + vnd_strerror(verr) : vnd_strsyserror(syserr)); + } +} + +static void +vnd_libwarn(vnd_errno_t verr, int syserr, const char *format, ...) +{ + va_list alist; + + va_start(alist, format); + vnd_vwarn(verr, syserr, format, alist); + va_end(alist); +} + +static void +vnd_warn(const char *format, ...) +{ + va_list alist; + + va_start(alist, format); + vnd_vwarn(0, 0, format, alist); + va_end(alist); +} + +static vndadm_proptbl_t vndadm_propname_tbl[] = { + { "rxbuf", vndadm_print_size, + vndadm_parse_size }, /* VND_PROP_RXBUF */ + { "txbuf", vndadm_print_size, + vndadm_parse_size }, /* VND_PROP_TXBUF */ + { "maxsize", vndadm_print_size, NULL }, /* VND_PROP_MAXBUF */ + { "mintu", vndadm_print_number, NULL }, /* VND_PROP_MINTU */ + { "maxtu", vndadm_print_number, NULL }, /* VND_PROP_MAXTU */ + NULL /* VND_PROP_MAX */ +}; + +static const char * +vndadm_prop_to_name(vnd_prop_t prop) +{ + if (prop > VND_PROP_MAX) + return (NULL); + + return (vndadm_propname_tbl[prop].vp_name); +} + +static vnd_prop_t +vndadm_name_to_prop(const char *name) +{ + int i; + + for (i = 0; i < VND_PROP_MAX; i++) { + if (strcmp(name, vndadm_propname_tbl[i].vp_name) == 0) + return (i); + } + + return (VND_PROP_MAX); +} + +static int +vndadm_print_size(vnd_handle_t *vhp, vnd_prop_t prop) +{ + vnd_prop_buf_t buf; + + if (vnd_prop_get(vhp, prop, &buf, sizeof (buf)) != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to get property %s", vndadm_prop_to_name(prop)); + return (1); + } + + (void) printf("%lld", buf.vpb_size); + return (0); +} + +static int +vndadm_print_number(vnd_handle_t *vhp, vnd_prop_t prop) +{ + vnd_prop_buf_t buf; + + if (vnd_prop_get(vhp, prop, &buf, sizeof (buf)) != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to get property %s", vndadm_prop_to_name(prop)); + return (1); + } + + (void) printf("%lld", buf.vpb_size); + return (0); +} + +static int +vndadm_parse_size(char *str, void **bufp, size_t *sizep) +{ + char *end; + unsigned long long val, orig; + vnd_prop_buf_t *buf; + + errno = 0; + val = strtoull(str, &end, 10); + if (errno != 0) { + vnd_warn("%s: not a number\n", str); + return (1); + } + + orig = val; + switch (*end) { + case 'g': + case 'G': + val *= 1024; + if (val < orig) + goto overflow; + /*FALLTHRU*/ + case 'm': + case 'M': + val *= 1024; + if (val < orig) + goto overflow; + /*FALLTHRU*/ + case 'k': + case 'K': + val *= 1024; + if (val < orig) + goto overflow; + end++; + break; + default: + break; + } + + if (*end == 'b' || *end == 'B') + end++; + if (*end != '\0') { + vnd_warn("%s: not a number", str); + return (1); + } + + buf = malloc(sizeof (vnd_prop_buf_t)); + if (buf == NULL) { + vnd_warn("failed to allocate memory for setting a property"); + return (1); + } + + buf->vpb_size = val; + *bufp = buf; + *sizep = sizeof (vnd_prop_buf_t); + + return (0); + +overflow: + vnd_warn("value overflowed: %s\n", str); + return (1); +} + +static void +vndadm_create_usage(FILE *out) +{ + (void) fprintf(out, "\tcreate:\t\t[-z zonename] -l datalink name\n"); +} + +static int +vndadm_create(int argc, char *argv[]) +{ + int c, syserr; + vnd_errno_t vnderr; + const char *datalink = NULL; + const char *linkname = NULL; + const char *zonename = NULL; + vnd_handle_t *vhp; + + optind = 0; + while ((c = getopt(argc, argv, ":z:l:")) != -1) { + switch (c) { + case 'l': + datalink = optarg; + break; + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + return (usage("missing required link name\n")); + } else if (argc > 1) { + return (usage("create: too many arguments for link name, " + "pick one\n")); + } + linkname = argv[0]; + if (datalink == NULL) + datalink = linkname; + + vhp = vnd_create(zonename, datalink, linkname, &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, + "failed to create datapath link %s", linkname); + return (1); + } + + vnd_close(vhp); + return (0); +} + +static void +vndadm_destroy_usage(FILE *out) +{ + (void) fprintf(out, "\tdestroy:\t[-z zonename] [link]...\n"); +} + +static int +vndadm_destroy(int argc, char *argv[]) +{ + vnd_handle_t *vhp; + int c, syserr; + vnd_errno_t vnderr; + const char *zonename = NULL; + + optind = 0; + while ((c = getopt(argc, argv, ":z:")) != -1) { + switch (c) { + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + return (usage("extraneous arguments\n")); + } + + vhp = vnd_open(zonename, argv[0], &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, "failed to open link: %s", argv[0]); + return (1); + } + + if (vnd_unlink(vhp) != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to destroy link %s", argv[0]); + return (1); + } + + vnd_close(vhp); + return (0); +} + +static void +vndadm_list_usage(FILE *out) +{ + (void) fprintf(out, "\tlist:\t\t[-p] [-d delim] [-o field,...] " + "[-z zonename] [link]...\n"); +} + +#define VNDADM_LIST_NFIELDS 3 + +typedef struct vndadm_list_cb { + int vsc_argc; + char **vsc_argv; + int vsc_found; + boolean_t vsc_parse; + const char *vsc_delim; + int vsc_order[VNDADM_LIST_NFIELDS]; + int vsc_last; + zoneid_t vsc_zid; +} vndadm_list_cb_t; + +typedef struct vndadm_list_field { + const char *vlf_name; + const char *vlf_header; + int vlf_size; + void (*vlf_print)(struct vndadm_list_field *, vnd_info_t *, boolean_t); + void (*vlf_parse)(struct vndadm_list_field *, vnd_info_t *, boolean_t); +} vndadm_list_field_t; + +static void +vlf_print_link(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + if (last == B_TRUE) { + (void) printf("%s", viip->vi_name); + } else { + (void) printf("%-*s", vlfp->vlf_size, viip->vi_name); + } +} + +/* ARGSUSED */ +static void +vlf_parse_link(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + (void) printf("%s", viip->vi_name); +} + +static void +vlf_print_datalink(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + if (last == B_TRUE) { + (void) printf("%s", viip->vi_datalink); + } else { + (void) printf("%-*s", vlfp->vlf_size, viip->vi_datalink); + } +} + +/* ARGSUSED */ +static void +vlf_parse_datalink(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + (void) printf("%s", viip->vi_datalink); +} + +static void +vlf_print_zone(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + char buf[ZONENAME_MAX]; + + if (getzonenamebyid(viip->vi_zone, buf, sizeof (buf)) <= 0) + (void) strlcpy(buf, "<unknown>", sizeof (buf)); + + if (last == B_TRUE) { + (void) printf("%s", buf); + } else { + (void) printf("%-*s", vlfp->vlf_size, buf); + } +} + +/* ARGSUSED */ +static void +vlf_parse_zone(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + char buf[ZONENAME_MAX]; + + if (getzonenamebyid(viip->vi_zone, buf, sizeof (buf)) <= 0) + (void) strlcpy(buf, "<unknown>", sizeof (buf)); + + (void) printf("%s", buf); +} + +static vndadm_list_field_t vlf_tbl[] = { + { "name", "NAME", 16, vlf_print_link, vlf_parse_link }, + { "datalink", "DATALINK", 16, vlf_print_datalink, vlf_parse_datalink }, + { "zone", "ZONENAME", 32, vlf_print_zone, vlf_parse_zone }, + { NULL } +}; + + +static int +vndadm_list_f(vnd_info_t *viip, void *arg) +{ + int i; + boolean_t found; + vndadm_list_cb_t *vscp = arg; + + if (vscp->vsc_zid != ALL_ZONES && vscp->vsc_zid != viip->vi_zone) + return (0); + + if (vscp->vsc_argc != 0) { + found = B_FALSE; + for (i = 0; i < vscp->vsc_argc; i++) { + if (strcmp(viip->vi_name, vscp->vsc_argv[i]) == 0) { + found = B_TRUE; + break; + } + } + if (found == B_FALSE) + return (0); + vscp->vsc_found++; + } + + for (i = 0; i < VNDADM_LIST_NFIELDS && vscp->vsc_order[i] != -1; i++) { + boolean_t last = i == vscp->vsc_last; + if (vscp->vsc_parse == B_TRUE) + vlf_tbl[vscp->vsc_order[i]].vlf_parse( + &vlf_tbl[vscp->vsc_order[i]], viip, last); + else + vlf_tbl[vscp->vsc_order[i]].vlf_print( + &vlf_tbl[vscp->vsc_order[i]], viip, last); + + if (last == B_FALSE) + (void) printf("%s", vscp->vsc_delim); + } + (void) printf("\n"); + + return (0); +} + +static int +vndadm_list(int argc, char *argv[]) +{ + int c, i, syserr; + vnd_errno_t vnderr; + boolean_t parse = B_FALSE; + const char *zonename = NULL, *delim = NULL; + char *fields = NULL; + vndadm_list_cb_t vsc; + + optind = 0; + while ((c = getopt(argc, argv, ":pd:o:z:")) != -1) { + switch (c) { + case 'p': + parse = B_TRUE; + break; + case 'd': + delim = optarg; + break; + case 'o': + fields = optarg; + break; + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + vsc.vsc_argc = argc; + vsc.vsc_argv = argv; + vsc.vsc_found = 0; + if (zonename != NULL) { + vsc.vsc_zid = getzoneidbyname(zonename); + if (vsc.vsc_zid == -1) { + vnd_warn("no such zone: %s\n", zonename); + return (1); + } + } else { + vsc.vsc_zid = ALL_ZONES; + } + + /* Sanity check parseable related stuff */ + if (delim != NULL && parse == B_FALSE) { + return (usage("-d cannot be used without -p\n")); + } + + if (parse == B_TRUE && fields == NULL) { + return (usage("-p cannot be used without -o\n")); + } + + /* validate our fields, if any */ + if (fields != NULL) { + char *c, *n; + int floc = 0; + + c = fields; + for (;;) { + if (floc >= VNDADM_LIST_NFIELDS) { + return (usage("too many fields specified " + "for -o\n")); + } + + n = strchr(c, ','); + if (n != NULL) + *n = '\0'; + + for (i = 0; i < VNDADM_LIST_NFIELDS; i++) { + if (strcasecmp(c, vlf_tbl[i].vlf_name) == 0) + break; + } + if (i == VNDADM_LIST_NFIELDS) { + vnd_warn("invalid field for -o: %s\nvalid " + "fields are:", c); + for (i = 0; i < VNDADM_LIST_NFIELDS; i++) + vnd_warn(" %s", vlf_tbl[i].vlf_name); + vnd_warn("\n"); + return (usage(NULL)); + } + vsc.vsc_order[floc] = i; + floc++; + + if (n == NULL) + break; + c = n + 1; + } + + vsc.vsc_last = floc - 1; + while (floc < VNDADM_LIST_NFIELDS) + vsc.vsc_order[floc++] = -1; + } else { + vsc.vsc_order[0] = 0; + vsc.vsc_order[1] = 1; + vsc.vsc_order[2] = 2; + } + + vsc.vsc_parse = parse; + vsc.vsc_delim = delim; + if (vsc.vsc_delim == NULL) + vsc.vsc_delim = " "; + + if (vsc.vsc_parse != B_TRUE) { + for (i = 0; i < VNDADM_LIST_NFIELDS && vsc.vsc_order[i] != -1; + i++) { + if (i + 1 == VNDADM_LIST_NFIELDS) { + (void) printf("%s\n", + vlf_tbl[vsc.vsc_order[i]].vlf_header); + continue; + } + (void) printf("%-*s ", + vlf_tbl[vsc.vsc_order[i]].vlf_size, + vlf_tbl[vsc.vsc_order[i]].vlf_header); + } + } + + if (vnd_walk(vndadm_list_f, &vsc, &vnderr, &syserr) != 0) { + vnd_libwarn(vnderr, syserr, "failed to walk vnd links"); + return (1); + } + + if (argc > 0 && vsc.vsc_found == 0) { + vnd_warn("no links matched requested names\n"); + return (1); + } + + return (0); +} + +typedef struct vndadm_get { + boolean_t vg_parse; + const char *vg_delim; + const char *vg_link; + int vg_argc; + char **vg_argv; +} vndadm_get_t; + +static int +vndadm_get_cb(vnd_handle_t *vhp, vnd_prop_t prop, void *arg) +{ + boolean_t writeable; + const char *perm; + vndadm_get_t *vgp = arg; + const char *name = vndadm_prop_to_name(prop); + + /* Verify if this is a prop we're supposed to print */ + if (vgp->vg_argc > 0) { + int i; + boolean_t found = B_FALSE; + for (i = 0; i < vgp->vg_argc; i++) { + if (strcmp(name, vgp->vg_argv[i]) == 0) { + found = B_TRUE; + break; + } + } + if (found == B_FALSE) + return (0); + } + + if (vnd_prop_writeable(prop, &writeable) != 0) + abort(); + + perm = writeable ? "rw" : "r-"; + + if (vgp->vg_parse == B_TRUE) { + (void) printf("%s%s%s%s%s%s", vgp->vg_link, vgp->vg_delim, + name, vgp->vg_delim, perm, vgp->vg_delim); + } else { + (void) printf("%-13s %-16s %-5s ", vgp->vg_link, name, perm); + } + + if (vndadm_propname_tbl[prop].vp_print != NULL) { + if (vndadm_propname_tbl[prop].vp_print(vhp, prop) != 0) + return (1); + } else { + (void) printf("-"); + } + (void) printf("\n"); + return (0); +} + +static int +vndadm_get(int argc, char *argv[]) +{ + vnd_handle_t *vhp; + boolean_t parse = B_FALSE; + vndadm_get_t vg; + int c, syserr; + vnd_errno_t vnderr; + const char *zonename = NULL, *delim = NULL; + + if (argc <= 0) { + return (usage("get requires a link name\n")); + } + + optind = 0; + while ((c = getopt(argc, argv, ":pd:z:")) != -1) { + switch (c) { + case 'p': + parse = B_TRUE; + break; + case 'd': + delim = optarg; + break; + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + return (usage("missing required link\n")); + } + + vhp = vnd_open(zonename, argv[0], &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, "failed to open link: %s", argv[0]); + return (1); + } + + vg.vg_argc = argc - 1; + vg.vg_argv = argv + 1; + vg.vg_link = argv[0]; + vg.vg_parse = parse; + vg.vg_delim = delim != NULL ? delim : " "; + if (vg.vg_parse == B_FALSE) + (void) printf("%-13s %-16s %-5s %s\n", "LINK", "PROPERTY", + "PERM", "VALUE"); + + if (vnd_prop_iter(vhp, vndadm_get_cb, &vg) != 0) + return (1); + + return (0); +} + +static void +vndadm_get_usage(FILE *out) +{ + (void) fprintf(out, + "\tget:\t\t[-p] [-d delim] [-z zonename] link [prop]...\n"); +} + +static int +vndadm_set(int argc, char *argv[]) +{ + vnd_handle_t *vhp; + int c, i, syserr; + vnd_errno_t vnderr; + const char *zonename = NULL; + + optind = 0; + while ((c = getopt(argc, argv, ":z:")) != -1) { + switch (c) { + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 2) { + return (usage("missing arguments to set\n")); + } + + vhp = vnd_open(zonename, argv[0], &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, "failed to open link: %s", argv[0]); + return (1); + } + + for (i = 1; i < argc; i++) { + char *eq, *key, *value; + boolean_t writeable; + vnd_prop_t prop; + void *buf; + size_t psize; + int ret; + + key = argv[i]; + eq = strchr(key, '='); + if (eq == NULL) { + vnd_warn("invalid property name=value: %s\n", key); + return (1); + } + *eq = '\0'; + value = eq + 1; + if (*value == '\0') { + vnd_warn("property value required for %s\n", key); + return (1); + } + prop = vndadm_name_to_prop(key); + if (prop == VND_PROP_MAX) { + vnd_warn("unknown property: %s\n", key); + return (1); + } + + if (vnd_prop_writeable(prop, &writeable) != 0) + abort(); + if (writeable != B_TRUE) { + vnd_warn("property %s is read-only\n", key); + return (1); + } + assert(vndadm_propname_tbl[prop].vp_parse != NULL); + + /* + * vp_parse functions should say what explicitly is invalid. We + * should indicate that the property failed. + */ + ret = vndadm_propname_tbl[prop].vp_parse(value, &buf, &psize); + if (ret != 0) { + vnd_warn("failed to set property %s\n", key); + return (1); + } + + ret = vnd_prop_set(vhp, prop, buf, psize); + free(buf); + if (ret != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to set property %s", key); + return (1); + } + } + + return (0); +} + +static void +vndadm_set_usage(FILE *out) +{ + (void) fprintf(out, "\tset:\t\t[-z zonename] link prop=val...\n"); +} + +typedef struct vnd_cmdtab { + const char *vc_name; + int (*vc_op)(int, char *[]); + void (*vc_usage)(FILE *); +} vnd_cmdtab_t; + +static vnd_cmdtab_t vnd_tab[] = { + { "create", vndadm_create, vndadm_create_usage }, + { "destroy", vndadm_destroy, vndadm_destroy_usage }, + { "list", vndadm_list, vndadm_list_usage }, + { "get", vndadm_get, vndadm_get_usage }, + { "set", vndadm_set, vndadm_set_usage }, + { NULL, NULL } +}; + +static int +usage(const char *format, ...) +{ + vnd_cmdtab_t *tab; + const char *help = "usage: %s <subcommand> <args> ...\n"; + + if (format != NULL) { + va_list alist; + + va_start(alist, format); + (void) fprintf(stderr, "%s: ", vnd_pname); + (void) vfprintf(stderr, format, alist); + va_end(alist); + } + (void) fprintf(stderr, help, vnd_pname); + for (tab = vnd_tab; tab->vc_name != NULL; tab++) + tab->vc_usage(stderr); + + return (2); +} + +int +main(int argc, char *argv[]) +{ + vnd_cmdtab_t *tab; + + vnd_pname = basename(argv[0]); + if (argc < 2) { + return (usage(NULL)); + } + + for (tab = vnd_tab; tab->vc_name != NULL; tab++) { + if (strcmp(argv[1], tab->vc_name) == 0) { + argc -= 2; argv += 2; + assert(argc >= 0); + return (tab->vc_op(argc, argv)); + } + } + + return (usage("unknown sub-command '%s'\n", argv[1])); +} diff --git a/usr/src/cmd/vndstat/Makefile b/usr/src/cmd/vndstat/Makefile new file mode 100644 index 0000000000..c77eef3887 --- /dev/null +++ b/usr/src/cmd/vndstat/Makefile @@ -0,0 +1,33 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +PROG= vndstat + +include ../Makefile.cmd + +LDLIBS += -lkstat + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + $(RM) $(PROG) + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/vndstat/vndstat.c b/usr/src/cmd/vndstat/vndstat.c new file mode 100644 index 0000000000..6f6c76fc12 --- /dev/null +++ b/usr/src/cmd/vndstat/vndstat.c @@ -0,0 +1,542 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ + +#include <sys/kstat.h> +#include <kstat.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <alloca.h> +#include <signal.h> +#include <sys/varargs.h> +#include <sys/int_limits.h> +#include <sys/sysmacros.h> + +#define KSTAT_FIELD_USEINSTANCE 0x01 +#define KSTAT_FIELD_NODELTA 0x02 +#define KSTAT_FIELD_FILLER 0x04 +#define KSTAT_FIELD_STRING 0x08 +#define KSTAT_FIELD_UNIT 0x10 +#define KSTAT_FIELD_LJUST 0x20 + +typedef struct kstat_field { + char *ksf_header; /* header for field */ + char *ksf_name; /* name of stat, if any */ + int ksf_width; /* width for field in output line */ + uint32_t ksf_flags; /* flags for this field, if any */ + char *ksf_suffix; /* optional suffix for units */ + int ksf_hint; /* index hint for field in kstat */ +} kstat_field_t; + +typedef struct kstat_instance { + char ksi_name[KSTAT_STRLEN]; /* name of the underlying kstat */ + int ksi_instance; /* instance identifer of this kstat */ + kstat_t *ksi_ksp; /* pointer to the kstat */ + uint64_t *ksi_data[2]; /* pointer to two generations of data */ + hrtime_t ksi_snaptime[2]; /* hrtime for data generations */ + int ksi_gen; /* current generation */ + struct kstat_instance *ksi_next; /* next in instance list */ +} kstat_instance_t; + +const char *g_cmd = "vndstat"; + +static void +kstat_nicenum(uint64_t num, char *buf, size_t buflen) +{ + uint64_t n = num; + int index = 0; + char u; + + while (n >= 1024) { + n /= 1024; + index++; + } + + u = " KMGTPE"[index]; + + if (index == 0) { + (void) snprintf(buf, buflen, "%llu", n); + } else if ((num & ((1ULL << 10 * index) - 1)) == 0) { + /* + * If this is an even multiple of the base, always display + * without any decimal precision. + */ + (void) snprintf(buf, buflen, "%llu%c", n, u); + } else { + /* + * We want to choose a precision that reflects the best choice + * for fitting in 5 characters. This can get rather tricky when + * we have numbers that are very close to an order of magnitude. + * For example, when displaying 10239 (which is really 9.999K), + * we want only a single place of precision for 10.0K. We could + * develop some complex heuristics for this, but it's much + * easier just to try each combination in turn. + */ + int i; + for (i = 2; i >= 0; i--) { + if (snprintf(buf, buflen, "%.*f%c", i, + (double)num / (1ULL << 10 * index), u) <= 5) + break; + } + } +} + +static void +fatal(char *fmt, ...) +{ + va_list ap; + int error = errno; + + va_start(ap, fmt); + + (void) fprintf(stderr, "%s: ", g_cmd); + /*LINTED*/ + (void) vfprintf(stderr, fmt, ap); + + if (fmt[strlen(fmt) - 1] != '\n') + (void) fprintf(stderr, ": %s\n", strerror(error)); + + exit(EXIT_FAILURE); +} + +int +kstat_field_hint(kstat_t *ksp, kstat_field_t *field) +{ + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + int i; + + assert(ksp->ks_type == KSTAT_TYPE_NAMED); + + for (i = 0; i < ksp->ks_ndata; i++) { + if (strcmp(field->ksf_name, nm[i].name) == 0) + return (field->ksf_hint = i); + } + + fatal("could not find field '%s' in %s:%d\n", + field->ksf_name, ksp->ks_name, ksp->ks_instance); + + return (0); +} + +int +kstat_instances_compare(const void *lhs, const void *rhs) +{ + kstat_instance_t *l = *((kstat_instance_t **)lhs); + kstat_instance_t *r = *((kstat_instance_t **)rhs); + int rval; + + if ((rval = strcmp(l->ksi_name, r->ksi_name)) != 0) + return (rval); + + if (l->ksi_instance < r->ksi_instance) + return (-1); + + if (l->ksi_instance > r->ksi_instance) + return (1); + + return (0); +} + +void +kstat_instances_update(kstat_ctl_t *kcp, kstat_instance_t **head, + boolean_t (*interested)(kstat_t *)) +{ + int ninstances = 0, i; + kstat_instance_t **sorted, *ksi, *next; + kstat_t *ksp; + kid_t kid; + + if ((kid = kstat_chain_update(kcp)) == 0 && *head != NULL) + return; + + if (kid == -1) + fatal("failed to update kstat chain"); + + for (ksi = *head; ksi != NULL; ksi = ksi->ksi_next) + ksi->ksi_ksp = NULL; + + for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + kstat_instance_t *last = NULL; + + if (!interested(ksp)) + continue; + + /* + * Now look to see if we have this instance and name. (Yes, + * this is a linear search; we're assuming that this list is + * modest in size.) + */ + for (ksi = *head; ksi != NULL; ksi = ksi->ksi_next) { + last = ksi; + + if (ksi->ksi_instance != ksp->ks_instance) + continue; + + if (strcmp(ksi->ksi_name, ksp->ks_name) != 0) + continue; + + ksi->ksi_ksp = ksp; + ninstances++; + break; + } + + if (ksi != NULL) + continue; + + if ((ksi = malloc(sizeof (kstat_instance_t))) == NULL) + fatal("could not allocate memory for stat instance"); + + bzero(ksi, sizeof (kstat_instance_t)); + (void) strlcpy(ksi->ksi_name, ksp->ks_name, KSTAT_STRLEN); + ksi->ksi_instance = ksp->ks_instance; + ksi->ksi_ksp = ksp; + ksi->ksi_next = NULL; + + if (last == NULL) { + assert(*head == NULL); + *head = ksi; + } else { + last->ksi_next = ksi; + } + + ninstances++; + } + + /* + * Now we know how many instances we have; iterate back over them, + * pruning the stale ones and adding the active ones to a holding + * array in which to sort them. + */ + sorted = (void *)alloca(ninstances * sizeof (kstat_instance_t *)); + ninstances = 0; + + for (ksi = *head; ksi != NULL; ksi = next) { + next = ksi->ksi_next; + + if (ksi->ksi_ksp == NULL) { + free(ksi); + } else { + sorted[ninstances++] = ksi; + } + } + + if (ninstances == 0) { + *head = NULL; + return; + } + + qsort(sorted, ninstances, sizeof (kstat_instance_t *), + kstat_instances_compare); + + *head = sorted[0]; + + for (i = 0; i < ninstances; i++) { + ksi = sorted[i]; + ksi->ksi_next = i < ninstances - 1 ? sorted[i + 1] : NULL; + } +} + +void +kstat_instances_read(kstat_ctl_t *kcp, kstat_instance_t *instances, + kstat_field_t *fields) +{ + kstat_instance_t *ksi; + int i, nfields; + + for (nfields = 0; fields[nfields].ksf_header != NULL; nfields++) + continue; + + for (ksi = instances; ksi != NULL; ksi = ksi->ksi_next) { + kstat_t *ksp = ksi->ksi_ksp; + + if (ksp == NULL) + continue; + + if (kstat_read(kcp, ksp, NULL) == -1) { + if (errno == ENXIO) { + /* + * Our kstat has been removed since the update; + * NULL it out to prevent us from trying to read + * it again (and to indicate that it should not + * be displayed) and drive on. + */ + ksi->ksi_ksp = NULL; + continue; + } + + fatal("failed to read kstat %s:%d", + ksi->ksi_name, ksi->ksi_instance); + } + + if (ksp->ks_type != KSTAT_TYPE_NAMED) { + fatal("%s:%d is not a named kstat", ksi->ksi_name, + ksi->ksi_instance); + } + + if (ksi->ksi_data[0] == NULL) { + size_t size = nfields * sizeof (uint64_t) * 2; + uint64_t *data; + + if ((data = malloc(size)) == NULL) + fatal("could not allocate memory"); + + bzero(data, size); + ksi->ksi_data[0] = data; + ksi->ksi_data[1] = &data[nfields]; + } + + for (i = 0; i < nfields; i++) { + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + kstat_field_t *field = &fields[i]; + int hint = field->ksf_hint; + + if (field->ksf_name == NULL) + continue; + + if (hint < 0 || hint >= ksp->ks_ndata || + strcmp(field->ksf_name, nm[hint].name) != 0) { + hint = kstat_field_hint(ksp, field); + } + + if (field->ksf_flags & KSTAT_FIELD_STRING) + ksi->ksi_data[ksi->ksi_gen][i] = + (uint64_t)(uintptr_t) + nm[hint].value.str.addr.ptr; + else + ksi->ksi_data[ksi->ksi_gen][i] = + nm[hint].value.ui64; + } + + ksi->ksi_snaptime[ksi->ksi_gen] = ksp->ks_snaptime; + ksi->ksi_gen ^= 1; + } +} + +uint64_t +kstat_instances_delta(kstat_instance_t *ksi, int i) +{ + int gen = ksi->ksi_gen; + uint64_t delta = ksi->ksi_data[gen ^ 1][i] - ksi->ksi_data[gen][i]; + uint64_t tdelta = ksi->ksi_snaptime[gen ^ 1] - ksi->ksi_snaptime[gen]; + + return (((delta * (uint64_t)NANOSEC) + (tdelta / 2)) / tdelta); +} + +void +kstat_instances_print(kstat_instance_t *instances, kstat_field_t *fields, + boolean_t header) +{ + kstat_instance_t *ksi = instances; + int i, nfields; + + for (nfields = 0; fields[nfields].ksf_header != NULL; nfields++) + continue; + + if (header) { + for (i = 0; i < nfields; i++) { + if (fields[i].ksf_flags & KSTAT_FIELD_LJUST) { + (void) printf("%s%c", fields[i].ksf_header, + i < nfields - 1 ? ' ' : '\n'); + continue; + } + (void) printf("%*s%c", fields[i].ksf_width, + fields[i].ksf_header, i < nfields - 1 ? ' ' : '\n'); + } + } + + for (ksi = instances; ksi != NULL; ksi = ksi->ksi_next) { + if (ksi->ksi_snaptime[1] == 0 || ksi->ksi_ksp == NULL) + continue; + + for (i = 0; i < nfields; i++) { + char trailer = i < nfields - 1 ? ' ' : '\n'; + + if (fields[i].ksf_flags & KSTAT_FIELD_FILLER) { + (void) printf("%*s%c", fields[i].ksf_width, + fields[i].ksf_header, trailer); + continue; + } + + if (fields[i].ksf_flags & KSTAT_FIELD_STRING) { + (void) printf("%*s%c", fields[i].ksf_width, + (char *)(uintptr_t)ksi->ksi_data[ + ksi->ksi_gen ^ 1][i], + trailer); + continue; + } + + if (fields[i].ksf_flags & KSTAT_FIELD_UNIT) { + char buf[128]; + size_t flen = fields[i].ksf_width + 1; + const char *suffix = ""; + + if (fields[i].ksf_suffix != NULL) { + suffix = fields[i].ksf_suffix; + flen -= strlen(fields[i].ksf_suffix); + } + + kstat_nicenum(fields[i].ksf_flags & + KSTAT_FIELD_NODELTA ? + ksi->ksi_data[ksi->ksi_gen ^ 1][i] : + kstat_instances_delta(ksi, i), buf, + MIN(sizeof (buf), flen)); + (void) printf("%*s%s%c", flen - 1, buf, + suffix, trailer); + continue; + } + + (void) printf("%*lld%c", fields[i].ksf_width, + fields[i].ksf_flags & KSTAT_FIELD_USEINSTANCE ? + ksi->ksi_instance : + fields[i].ksf_flags & KSTAT_FIELD_NODELTA ? + ksi->ksi_data[ksi->ksi_gen ^ 1][i] : + kstat_instances_delta(ksi, i), trailer); + } + } +} + +static boolean_t +interested(kstat_t *ksp) +{ + const char *module = "vnd"; + const char *class = "net"; + + if (strcmp(ksp->ks_module, module) != 0) + return (B_FALSE); + + if (strcmp(ksp->ks_class, class) != 0) + return (B_FALSE); + + return (B_TRUE); +} + +/* BEGIN CSTYLED */ +char *g_usage = "Usage: vndstat [interval [count]]\n" + "\n" + " Displays statistics for active vnd devices, with one line per device.\n" + " All statistics are reported as per-second rates.\n" + "\n" + " The columns are as follows:\n" + "\n" + " zone => name of the zone with the device\n" + " name => name of the vnd device\n" + " rx => bytes received\n" + " tx => bytes transmitted\n" + " drops => number of dropped packets\n" + " txfc => number of transmit flow control events\n" + "\n"; +/* END CSTYLED */ + +void +usage() +{ + (void) fprintf(stderr, "%s", g_usage); + exit(EXIT_FAILURE); +} + +/*ARGSUSED*/ +void +intr(int sig) +{} + +/*ARGSUSED*/ +int +main(int argc, char **argv) +{ + kstat_ctl_t *kcp; + kstat_instance_t *instances = NULL; + int i = 0; + int interval = 1; + int count = INT32_MAX; + struct itimerval itimer; + struct sigaction act; + sigset_t set; + char *endp; + + kstat_field_t fields[] = { + { "name", "linkname", 6, KSTAT_FIELD_STRING }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "rx B/s", "rbytes", 8, KSTAT_FIELD_UNIT, "B/s" }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "tx B/s", "obytes", 8, KSTAT_FIELD_UNIT, "B/s" }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "drops", "total_drops", 5 }, + { "txfc", "flowcontrol_events", 4 }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "zone", "zonename", 36, + KSTAT_FIELD_STRING | KSTAT_FIELD_LJUST }, + { NULL } + }; + + if (argc > 1) { + interval = strtol(argv[1], &endp, 10); + + if (*endp != '\0' || interval <= 0) + usage(); + } + + if (argc > 2) { + count = strtol(argv[2], &endp, 10); + + if (*endp != '\0' || count <= 0) + usage(); + } + + if ((kcp = kstat_open()) == NULL) + fatal("could not open /dev/kstat"); + + (void) sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = intr; + (void) sigaction(SIGALRM, &act, NULL); + + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &set, NULL); + + bzero(&itimer, sizeof (itimer)); + itimer.it_value.tv_sec = interval; + itimer.it_interval.tv_sec = interval; + + if (setitimer(ITIMER_REAL, &itimer, NULL) != 0) { + fatal("could not set timer to %d second%s", interval, + interval == 1 ? "" : "s"); + } + + (void) sigemptyset(&set); + + for (;;) { + kstat_instances_update(kcp, &instances, interested); + kstat_instances_read(kcp, instances, fields); + + if (i++ > 0) { + kstat_instances_print(instances, fields, + instances != NULL && instances->ksi_next == NULL ? + (((i - 2) % 20) == 0) : B_TRUE); + } + + if (i > count) + break; + + (void) sigsuspend(&set); + } + + /*NOTREACHED*/ + return (0); +} diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index ff04e3a1c6..e089d215c0 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -225,7 +225,7 @@ get_usage(zfs_help_t idx) "<filesystem|volume>@<snap>[%<snap>][,...]\n" "\tdestroy <filesystem|volume>#<bookmark>\n")); case HELP_GET: - return (gettext("\tget [-rHp] [-d max] " + return (gettext("\tget [-crHp] [-d max] " "[-o \"all\" | field[,...]]\n" "\t [-t type[,...]] [-s source[,...]]\n" "\t <\"all\" | property[,...]> " @@ -579,7 +579,7 @@ finish_progress(char *done) } /* - * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> + * zfs clone [-Fp] [-o prop=value] ... <snap> <fs | vol> * * Given an existing dataset, create a writable copy whose initial contents * are the same as the source. The newly created dataset maintains a @@ -587,12 +587,18 @@ finish_progress(char *done) * the clone exists. * * The '-p' flag creates all the non-existing ancestors of the target first. + * + * The '-F' flag retries the zfs_mount() operation as long as zfs_mount() is + * still returning EBUSY. Any callers which specify -F should be careful to + * ensure that no other process has a persistent hold on the mountpoint's + * directory. */ static int zfs_do_clone(int argc, char **argv) { zfs_handle_t *zhp = NULL; boolean_t parents = B_FALSE; + boolean_t keeptrying = B_FALSE; nvlist_t *props; int ret = 0; int c; @@ -601,8 +607,11 @@ zfs_do_clone(int argc, char **argv) nomem(); /* check options */ - while ((c = getopt(argc, argv, "o:p")) != -1) { + while ((c = getopt(argc, argv, "Fo:p")) != -1) { switch (c) { + case 'F': + keeptrying = B_TRUE; + break; case 'o': if (parseprop(props, optarg) != 0) return (1); @@ -663,9 +672,14 @@ zfs_do_clone(int argc, char **argv) clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET); if (clone != NULL) { - if (zfs_get_type(clone) != ZFS_TYPE_VOLUME) - if ((ret = zfs_mount(clone, NULL, 0)) == 0) + if (zfs_get_type(clone) != ZFS_TYPE_VOLUME) { + while ((ret = zfs_mount(clone, NULL, 0)) != 0) { + if (!keeptrying || errno != EBUSY) + break; + } + if (ret == 0) ret = zfs_share(clone); + } zfs_close(clone); } } @@ -889,12 +903,13 @@ badusage: } /* - * zfs destroy [-rRf] <fs, vol> + * zfs destroy [-rRfF] <fs, vol> * zfs destroy [-rRd] <snap> * * -r Recursively destroy all children * -R Recursively destroy all dependents, including clones * -f Force unmounting of any dependents + * -F Continue retrying on seeing EBUSY * -d If we can't destroy now, mark for deferred destruction * * Destroys the given dataset. By default, it will unmount any filesystems, @@ -904,6 +919,7 @@ badusage: typedef struct destroy_cbdata { boolean_t cb_first; boolean_t cb_force; + boolean_t cb_wait; boolean_t cb_recurse; boolean_t cb_error; boolean_t cb_doclones; @@ -987,13 +1003,18 @@ out: static int destroy_callback(zfs_handle_t *zhp, void *data) { - destroy_cbdata_t *cb = data; + destroy_cbdata_t *cbp = data; + struct timespec ts; + int err = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 500 * (NANOSEC / MILLISEC); const char *name = zfs_get_name(zhp); - if (cb->cb_verbose) { - if (cb->cb_parsable) { + if (cbp->cb_verbose) { + if (cbp->cb_parsable) { (void) printf("destroy\t%s\n", name); - } else if (cb->cb_dryrun) { + } else if (cbp->cb_dryrun) { (void) printf(gettext("would destroy %s\n"), name); } else { @@ -1008,13 +1029,10 @@ destroy_callback(zfs_handle_t *zhp, void *data) */ if (strchr(zfs_get_name(zhp), '/') == NULL && zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { - zfs_close(zhp); - return (0); - } - if (cb->cb_dryrun) { - zfs_close(zhp); - return (0); + goto out; } + if (cbp->cb_dryrun) + goto out; /* * We batch up all contiguous snapshots (even of different @@ -1023,23 +1041,66 @@ destroy_callback(zfs_handle_t *zhp, void *data) * because we must delete a clone before its origin. */ if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { - fnvlist_add_boolean(cb->cb_batchedsnaps, name); - } else { - int error = zfs_destroy_snaps_nvl(g_zfs, - cb->cb_batchedsnaps, B_FALSE); - fnvlist_free(cb->cb_batchedsnaps); - cb->cb_batchedsnaps = fnvlist_alloc(); - - if (error != 0 || - zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || - zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { - zfs_close(zhp); - return (-1); + fnvlist_add_boolean(cbp->cb_batchedsnaps, name); + goto out; + } + + if (cbp->cb_wait) + libzfs_print_on_error(g_zfs, B_FALSE); + + /* + * Unless instructed to retry on EBUSY, bail out on the first error. + * When retrying, try every 500ms until either succeeding or seeing a + * non-EBUSY error code. + */ + while ((err = zfs_destroy_snaps_nvl(g_zfs, + cbp->cb_batchedsnaps, B_FALSE)) != 0) { + if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) { + nanosleep(&ts, NULL); + continue; + } + (void) fprintf(stderr, "%s: %s\n", + libzfs_error_action(g_zfs), + libzfs_error_description(g_zfs)); + break; + } + + fnvlist_free(cbp->cb_batchedsnaps); + cbp->cb_batchedsnaps = fnvlist_alloc(); + + if (err != 0) + goto out; + + while ((err = zfs_unmount(zhp, NULL, + cbp->cb_force ? MS_FORCE : 0)) != 0) { + if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) { + (void) nanosleep(&ts, NULL); + continue; } + (void) fprintf(stderr, "%s: %s\n", + libzfs_error_action(g_zfs), + libzfs_error_description(g_zfs)); + break; } + if (err != 0) + goto out; + + while ((err = zfs_destroy(zhp, cbp->cb_defer_destroy)) != 0) { + if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) { + (void) nanosleep(&ts, NULL); + continue; + } + (void) fprintf(stderr, "%s: %s\n", + libzfs_error_action(g_zfs), + libzfs_error_description(g_zfs)); + break; + } + +out: + libzfs_print_on_error(g_zfs, B_TRUE); zfs_close(zhp); - return (0); + return (err); } static int @@ -1195,7 +1256,7 @@ zfs_do_destroy(int argc, char **argv) zfs_type_t type = ZFS_TYPE_DATASET; /* check options */ - while ((c = getopt(argc, argv, "vpndfrR")) != -1) { + while ((c = getopt(argc, argv, "vpndfFrR")) != -1) { switch (c) { case 'v': cb.cb_verbose = B_TRUE; @@ -1214,6 +1275,9 @@ zfs_do_destroy(int argc, char **argv) case 'f': cb.cb_force = B_TRUE; break; + case 'F': + cb.cb_wait = B_TRUE; + break; case 'r': cb.cb_recurse = B_TRUE; break; @@ -1584,8 +1648,11 @@ zfs_do_get(int argc, char **argv) cb.cb_type = ZFS_TYPE_DATASET; /* check options */ - while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { + while ((c = getopt(argc, argv, ":d:o:s:rt:Hcp")) != -1) { switch (c) { + case 'c': + libzfs_set_cachedprops(g_zfs, B_TRUE); + break; case 'p': cb.cb_literal = B_TRUE; break; @@ -3015,6 +3082,7 @@ zfs_do_list(int argc, char **argv) int types = ZFS_TYPE_DATASET; boolean_t types_specified = B_FALSE; char *fields = NULL; + zprop_list_t *pl; list_cbdata_t cb = { 0 }; char *value; int limit = 0; @@ -3126,6 +3194,18 @@ zfs_do_list(int argc, char **argv) != 0) usage(B_FALSE); + /* + * The default set of properties contains only properties which can be + * retrieved from the set of cached properties. If any user-specfied + * properties cannot be retrieved from that set, unset the cachedprops + * flags on the ZFS handle. + */ + libzfs_set_cachedprops(g_zfs, B_TRUE); + for (pl = cb.cb_proplist; pl != NULL; pl = pl->pl_next) { + if (zfs_prop_cacheable(pl->pl_prop)) + libzfs_set_cachedprops(g_zfs, B_FALSE); + } + cb.cb_first = B_TRUE; ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist, diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c index 296c32be01..17ea786f42 100644 --- a/usr/src/cmd/zlogin/zlogin.c +++ b/usr/src/cmd/zlogin/zlogin.c @@ -22,11 +22,12 @@ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 DEY Storage Systems, Inc. * Copyright (c) 2014 Gary Mills + * Copyright 2015 Joyent, Inc. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* - * zlogin provides three types of login which allow users in the global + * zlogin provides five types of login which allow users in the global * zone to access non-global zones. * * - "interactive login" is similar to rlogin(1); for example, the user could @@ -42,12 +43,22 @@ * In this mode, zlogin sets up pipes as the communication channel, and * 'su' is used to do the login setup work. * + * - "interactive command" is a combination of the above two modes where + * a command is provide like the non-interactive case, but the -i option is + * also provided to make things interactive. For example, the user could + * issue 'zlogin -i my-zone /bin/sh'. In this mode neither 'login -c' nor + * 'su root -c' is prepended to the command invocation. Because of this + * there will be no wtmpx login record within the zone. + * * - "console login" is the equivalent to accessing the tip line for a * zone. For example, the user can issue 'zlogin -C my-zone'. * In this mode, zlogin contacts the zoneadmd process via unix domain * socket. If zoneadmd is not running, it starts it. This allows the * console to be available anytime the zone is installed, regardless of * whether it is running. + * + * - "standalone-processs interactive" is specified with -I and connects to + * the zone's stdin, stdout and stderr zfd(7D) devices. */ #include <sys/socket.h> @@ -92,7 +103,8 @@ #include <auth_attr.h> #include <secdb.h> -static int masterfd; +static int masterfd = -1; +static int ctlfd = -1; static struct termios save_termios; static struct termios effective_termios; static int save_fd; @@ -101,12 +113,13 @@ static volatile int dead; static volatile pid_t child_pid = -1; static int interactive = 0; static priv_set_t *dropprivs; +static unsigned int connect_flags = 0; static int nocmdchar = 0; static int failsafe = 0; -static int disconnect = 0; static char cmdchar = '~'; static int quiet = 0; +static char zonebrand[MAXNAMELEN]; static int pollerr = 0; @@ -123,10 +136,12 @@ static boolean_t forced_login = B_FALSE; #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif -#define SUPATH "/usr/bin/su" +#define SUPATH1 "/usr/bin/su" +#define SUPATH2 "/bin/su" #define FAILSAFESHELL "/sbin/sh" #define DEFAULTSHELL "/sbin/sh" #define DEF_PATH "/usr/sbin:/usr/bin" +#define LX_DEF_PATH "/bin:/usr/sbin:/usr/bin" #define CLUSTER_BRAND_NAME "cluster" @@ -153,7 +168,7 @@ static boolean_t forced_login = B_FALSE; static void usage(void) { - (void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] " + (void) fprintf(stderr, gettext("usage: %s [-dinCEINQS] [-e cmdchar] " "[-l user] zonename [command [args ...] ]\n"), pname); exit(2); } @@ -248,19 +263,11 @@ postfork_dropprivs() } } -/* - * Create the unix domain socket and call the zoneadmd server; handshake - * with it to determine whether it will allow us to connect. - */ static int -get_console_master(const char *zname) +connect_zone_sock(const char *zname, const char *suffix) { int sockfd = -1; struct sockaddr_un servaddr; - char clientid[MAXPATHLEN]; - char handshake[MAXPATHLEN], c; - int msglen; - int i = 0, err = 0; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { zperror(gettext("could not create socket")); @@ -270,35 +277,44 @@ get_console_master(const char *zname) bzero(&servaddr, sizeof (servaddr)); servaddr.sun_family = AF_UNIX; (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), - "%s/%s.console_sock", ZONES_TMPDIR, zname); - + "%s/%s.%s", ZONES_TMPDIR, zname, suffix); if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1) { - zperror(gettext("Could not connect to zone console")); - goto bad; + zperror(gettext("Could not connect to zone")); + close(sockfd); + return (-1); } - masterfd = sockfd; + return (sockfd); +} + + +static int +handshake_zone_sock(int sockfd, unsigned int flags) +{ + char clientid[MAXPATHLEN]; + char handshake[MAXPATHLEN], c; + int msglen; + int i = 0, err = 0; - msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n", - getpid(), setlocale(LC_MESSAGES, NULL), disconnect); + msglen = snprintf(clientid, sizeof (clientid), "IDENT %s %u\n", + setlocale(LC_MESSAGES, NULL), flags); if (msglen >= sizeof (clientid) || msglen < 0) { zerror("protocol error"); - goto bad; + return (-1); } - if (write(masterfd, clientid, msglen) != msglen) { + if (write(sockfd, clientid, msglen) != msglen) { zerror("protocol error"); - goto bad; + return (-1); } - bzero(handshake, sizeof (handshake)); - /* * Take care not to accumulate more than our fill, and leave room for * the NUL at the end. */ - while ((err = read(masterfd, &c, 1)) == 1) { + bzero(handshake, sizeof (handshake)); + while ((err = read(sockfd, &c, 1)) == 1) { if (i >= (sizeof (handshake) - 1)) break; if (c == '\n') @@ -308,26 +324,48 @@ get_console_master(const char *zname) } /* - * If something went wrong during the handshake we bail; perhaps - * the server died off. + * If something went wrong during the handshake we bail. + * Perhaps the server died off. */ if (err == -1) { - zperror(gettext("Could not connect to zone console")); - goto bad; + zperror(gettext("Could not connect to zone")); + return (-1); } - if (strncmp(handshake, "OK", sizeof (handshake)) == 0) - return (0); + if (strncmp(handshake, "OK", sizeof (handshake)) != 0) { + zerror(gettext("Zone is already in use by process ID %s."), + handshake); + return (-1); + } - zerror(gettext("Console is already in use by process ID %s."), - handshake); -bad: - (void) close(sockfd); - masterfd = -1; - return (-1); + return (0); } - +static int +send_ctl_sock(const char *buf, size_t len) +{ + char rbuf[BUFSIZ]; + int i; + if (ctlfd == -1) { + return (-1); + } + if (write(ctlfd, buf, len) != len) { + return (-1); + } + /* read the response */ + for (i = 0; i < (BUFSIZ - 1); i++) { + char c; + if (read(ctlfd, &c, 1) != 1 || c == '\n' || c == '\0') { + break; + } + rbuf[i] = c; + } + rbuf[i+1] = '\0'; + if (strncmp("OK", rbuf, BUFSIZ) != 0) { + return (-1); + } + return (0); +} /* * Routines to handle pty creation upon zone entry and to shuttle I/O back * and forth between the two terminals. We also compute and store the @@ -516,8 +554,32 @@ sigwinch(int s) { struct winsize ws; - if (ioctl(0, TIOCGWINSZ, &ws) == 0) - (void) ioctl(masterfd, TIOCSWINSZ, &ws); + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + if (ctlfd != -1) { + char buf[BUFSIZ]; + snprintf(buf, sizeof (buf), "TIOCSWINSZ %hu %hu\n", + ws.ws_row, ws.ws_col); + (void) send_ctl_sock(buf, strlen(buf)); + } else { + (void) ioctl(masterfd, TIOCSWINSZ, &ws); + } + } +} + +/* + * Toggle zfd EOF mode and notify zoneadmd + */ +/*ARGSUSED*/ +static void +sigusr1(int s) +{ + connect_flags ^= ZLOGIN_ZFD_EOF; + if (ctlfd != -1) { + char buf[BUFSIZ]; + snprintf(buf, sizeof (buf), "SETFLAGS %u\n", + connect_flags); + (void) send_ctl_sock(buf, strlen(buf)); + } } static volatile int close_on_sig = -1; @@ -862,28 +924,28 @@ doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd, int sig_fd, break; } - /* event from master side stdout */ - if (pollfds[0].revents) { - if (pollfds[0].revents & + /* event from master side stderr */ + if (pollfds[1].revents) { + if (pollfds[1].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - if (process_output(stdout_fd, STDOUT_FILENO) + if (process_output(stderr_fd, STDERR_FILENO) != 0) break; } else { - pollerr = pollfds[0].revents; + pollerr = pollfds[1].revents; break; } } - /* event from master side stderr */ - if (pollfds[1].revents) { - if (pollfds[1].revents & + /* event from master side stdout */ + if (pollfds[0].revents) { + if (pollfds[0].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - if (process_output(stderr_fd, STDERR_FILENO) + if (process_output(stdout_fd, STDOUT_FILENO) != 0) break; } else { - pollerr = pollfds[1].revents; + pollerr = pollfds[0].revents; break; } } @@ -1053,7 +1115,7 @@ zone_login_cmd(brand_handle_t bh, const char *login) * but we're going to be very simplistic about it and break stuff * up based on spaces. We're not even going to support any kind * of quoting or escape characters. It's truly amazing that - * there is no library function in OpenSolaris to do this for us. + * there is no library function in Illumos to do this for us. */ /* @@ -1099,7 +1161,7 @@ zone_login_cmd(brand_handle_t bh, const char *login) * checks). */ static char ** -prep_args(brand_handle_t bh, const char *login, char **argv) +prep_args(brand_handle_t bh, char *zonename, const char *login, char **argv) { int argc = 0, a = 0, i, n = -1; char **new_argv; @@ -1118,29 +1180,66 @@ prep_args(brand_handle_t bh, const char *login, char **argv) return (NULL); for (i = 0; i < argc; i++) { + if (i > 0) + (void) strcat(subshell, " "); (void) strcat(subshell, argv[i]); - (void) strcat(subshell, " "); } if (failsafe) { n = 4; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); + } else { + n = 6; + } + + if ((new_argv = malloc(sizeof (char *) * n)) == NULL) + return (NULL); + if (failsafe) { new_argv[a++] = FAILSAFESHELL; + new_argv[a++] = "-c"; } else { - n = 5; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) + struct stat sb; + char zonepath[MAXPATHLEN]; + char supath[MAXPATHLEN]; + + /* + * We allocated an extra slot in case our login below + * is not 'root' but normally we don't take that code + * path. + */ + n--; + + if (zone_get_zonepath(zonename, zonepath, + sizeof (zonepath)) != Z_OK) { + zerror(gettext("unable to determine zone " + "path")); return (NULL); + } + + (void) snprintf(supath, sizeof (supath), "%s/root/%s", + zonepath, SUPATH1); + if (stat(supath, &sb) == 0) { + new_argv[a++] = SUPATH1; + } else { + (void) snprintf(supath, sizeof (supath), + "%s/root/%s", zonepath, SUPATH2); + if (stat(supath, &sb) == 0) { + new_argv[a++] = SUPATH2; + } else { + zerror(gettext("unable to find 'su' " + "command")); + return (NULL); + } + } - new_argv[a++] = SUPATH; if (strcmp(login, "root") != 0) { new_argv[a++] = "-"; n++; } new_argv[a++] = (char *)login; + new_argv[a++] = "-c"; } - new_argv[a++] = "-c"; + new_argv[a++] = subshell; new_argv[a++] = NULL; assert(a == n); @@ -1185,6 +1284,7 @@ prep_env() int e = 0, size = 1; char **new_env, *estr; char *term = getenv("TERM"); + char *path; size++; /* for $PATH */ if (term != NULL) @@ -1201,7 +1301,12 @@ prep_env() if ((new_env = malloc(sizeof (char *) * size)) == NULL) return (NULL); - if ((estr = add_env("PATH", DEF_PATH)) == NULL) + if (strcmp(zonebrand, "lx") == 0) + path = LX_DEF_PATH; + else + path = DEF_PATH; + + if ((estr = add_env("PATH", path)) == NULL) return (NULL); new_env[e++] = estr; @@ -1723,24 +1828,55 @@ get_username() return (nptr->pw_name); } +static boolean_t +zlog_mode_logging(char *zonename) +{ + boolean_t lm = B_FALSE; + zone_dochandle_t handle; + struct zone_attrtab attr; + + if ((handle = zonecfg_init_handle()) == NULL) + return (lm); + + if (zonecfg_get_handle(zonename, handle) != Z_OK) + goto done; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp("zlog-mode", attr.zone_attr_name) == 0) { + if (strncmp("log", attr.zone_attr_value, 3) == 0) + lm = B_TRUE; + break; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); + return (lm); +} + int main(int argc, char **argv) { - int arg, console = 0; + int arg, console = 0, imode = 0; + int estatus = 0; zoneid_t zoneid; zone_state_t st; char *login = "root"; + int iflag = 0; int lflag = 0; int nflag = 0; char *zonename = NULL; char **proc_args = NULL; char **new_args, **new_env; sigset_t block_cld; + siginfo_t si; char devroot[MAXPATHLEN]; char *slavename, slaveshortname[MAXPATHLEN]; priv_set_t *privset; int tmpl_fd; - char zonebrand[MAXNAMELEN]; char default_brand[MAXNAMELEN]; struct stat sb; char kernzone[ZONENAME_MAX]; @@ -1754,7 +1890,7 @@ main(int argc, char **argv) (void) getpname(argv[0]); username = get_username(); - while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) { + while ((arg = getopt(argc, argv, "diNnECIR:Se:l:Q")) != EOF) { switch (arg) { case 'C': console = 1; @@ -1762,6 +1898,16 @@ main(int argc, char **argv) case 'E': nocmdchar = 1; break; + case 'I': + /* + * interactive mode is just a slight variation on the + * console mode. + */ + console = 1; + imode = 1; + /* The default is HUP, disconnect on EOF */ + connect_flags ^= ZLOGIN_ZFD_EOF; + break; case 'R': /* undocumented */ if (*optarg != '/') { zerror(gettext("root path must be absolute.")); @@ -1781,15 +1927,22 @@ main(int argc, char **argv) failsafe = 1; break; case 'd': - disconnect = 1; + connect_flags |= ZLOGIN_DISCONNECT; break; case 'e': set_cmdchar(optarg); break; + case 'i': + iflag = 1; + break; case 'l': login = optarg; lflag = 1; break; + case 'N': + /* NOHUP - do not send EOF */ + connect_flags ^= ZLOGIN_ZFD_EOF; + break; case 'n': nflag = 1; break; @@ -1800,6 +1953,12 @@ main(int argc, char **argv) if (console != 0) { + /* + * The only connect option in console mode is ZLOGIN_DISCONNECT + */ + if (imode == 0) + connect_flags &= ZLOGIN_DISCONNECT; + if (lflag != 0) { zerror(gettext( "-l may not be specified for console login")); @@ -1826,17 +1985,27 @@ main(int argc, char **argv) } + if (iflag != 0 && nflag != 0) { + zerror(gettext("-i and -n flags are incompatible")); + usage(); + } + if (failsafe != 0 && lflag != 0) { zerror(gettext("-l may not be specified for failsafe login")); usage(); } - if (!console && disconnect != 0) { + if (!console && (connect_flags & ZLOGIN_DISCONNECT) != 0) { zerror(gettext( "-d may only be specified with console login")); usage(); } + if (imode == 0 && (connect_flags & ZLOGIN_ZFD_EOF) != 0) { + zerror(gettext("-N may only be specified with -I")); + usage(); + } + if (optind == (argc - 1)) { /* * zone name, no process name; this should be an interactive @@ -1859,7 +2028,8 @@ main(int argc, char **argv) /* zone name and process name, and possibly some args */ zonename = argv[optind]; proc_args = &argv[optind + 1]; - interactive = 0; + if (iflag && isatty(STDIN_FILENO)) + interactive = 1; } else { usage(); } @@ -1945,10 +2115,16 @@ main(int argc, char **argv) } /* - * The console is a separate case from the rest of the code; handle - * it first. + * The console (or standalong interactive mode) is a separate case from + * the rest of the code; handle it first. */ if (console) { + int gz_stderr_fd = -1; + boolean_t set_raw = B_TRUE; + + if (imode && zlog_mode_logging(zonename)) + set_raw = B_FALSE; + /* * Ensure that zoneadmd for this zone is running. */ @@ -1958,15 +2134,49 @@ main(int argc, char **argv) /* * Make contact with zoneadmd. */ - if (get_console_master(zonename) == -1) - return (1); + if (!imode) { + masterfd = connect_zone_sock(zonename, "console_sock"); + if (masterfd == -1) { + return (1); + } + if (handshake_zone_sock(masterfd, + connect_flags) != 0) { + (void) close(masterfd); + return (1); + } + } else { + /* handshake with the control socket first */ + ctlfd = connect_zone_sock(zonename, "server_ctl"); + if (ctlfd == -1) { + return (1); + } + if (handshake_zone_sock(ctlfd, + connect_flags) != 0) { + (void) close(ctlfd); + return (1); + } + /* then open the io-related sockets */ + masterfd = connect_zone_sock(zonename, "server_out"); + gz_stderr_fd = connect_zone_sock(zonename, + "server_err"); + if (masterfd == -1 || gz_stderr_fd == -1) { + (void) close(ctlfd); + (void) close(masterfd); + (void) close(gz_stderr_fd); + return (1); + } + } - if (!quiet) - (void) printf( - gettext("[Connected to zone '%s' console]\n"), - zonename); + if (!quiet) { + if (imode) + (void) printf(gettext("[Connected to zone '%s' " + "interactively]\n"), zonename); + else + (void) printf(gettext("[Connected to zone '%s' " + "console]\n"), zonename); + } - if (set_tty_rawmode(STDIN_FILENO) == -1) { + if (set_raw && set_tty_rawmode(STDIN_FILENO) == -1) { reset_tty(); zperror(gettext("failed to set stdin pty to raw mode")); return (1); @@ -1975,15 +2185,25 @@ main(int argc, char **argv) (void) sigset(SIGWINCH, sigwinch); (void) sigwinch(0); + if (imode) { + /* Allow EOF mode toggling via SIGUSR1 */ + (void) sigset(SIGUSR1, sigusr1); + } + /* * Run the I/O loop until we get disconnected. */ - doio(masterfd, -1, masterfd, -1, -1, B_FALSE); + doio(masterfd, -1, masterfd, gz_stderr_fd, -1, B_FALSE); reset_tty(); - if (!quiet) - (void) printf( - gettext("\n[Connection to zone '%s' console " - "closed]\n"), zonename); + if (!quiet) { + if (imode) + (void) printf(gettext("\n[Interactive " + "connection to zone '%s' closed]\n"), + zonename); + else + (void) printf(gettext("\n[Connection to zone " + "'%s' console closed]\n"), zonename); + } return (0); } @@ -2051,11 +2271,23 @@ main(int argc, char **argv) return (1); } - if ((new_args = prep_args(bh, login, proc_args)) == NULL) { - zperror(gettext("could not assemble new arguments")); - brand_close(bh); - return (1); + /* + * The 'interactive' parameter (-i option) indicates that we're running + * a command interactively. In this case we skip prep_args so that we + * don't prepend the 'su root -c' preamble to the command invocation + * since the 'su' command typically will execute a setpgrp which will + * disassociate the actual command from the controlling terminal that + * we (zlogin) setup. + */ + if (!iflag) { + if ((new_args = prep_args(bh, zonename, login, proc_args)) + == NULL) { + zperror(gettext("could not assemble new arguments")); + brand_close(bh); + return (1); + } } + /* * Get the brand specific user_cmd. This command is used to get * a passwd(4) entry for login. @@ -2201,6 +2433,8 @@ main(int argc, char **argv) return (1); } + /* Note: we're now inside the zone, can't use gettext anymore */ + if (slavefd != STDERR_FILENO) (void) close(STDERR_FILENO); @@ -2242,8 +2476,18 @@ main(int argc, char **argv) /* * In failsafe mode, we don't use login(1), so don't try * setting up a utmpx entry. + * + * A branded zone may have very different utmpx semantics. + * At the moment, we only have two brand types: + * Illumos-like (native, sn1) and Linux. In the Illumos + * case, we know exactly how to do the necessary utmpx + * setup. Fortunately for us, the Linux /bin/login is + * prepared to deal with a non-initialized utmpx entry, so + * we can simply skip it. If future brands don't fall into + * either category, we'll have to add a per-brand utmpx + * setup hook. */ - if (!failsafe) + if (!failsafe && (strcmp(zonebrand, "lx") != 0)) if (setup_utmpx(slaveshortname) == -1) return (1); @@ -2252,13 +2496,17 @@ main(int argc, char **argv) * execute the brand's login program. */ if (setuid(0) == -1) { - zperror(gettext("insufficient privilege")); + zperror("insufficient privilege"); return (1); } - (void) execve(new_args[0], new_args, new_env); - zperror(gettext("exec failure")); - return (1); + if (iflag) { + (void) execve(proc_args[0], proc_args, new_env); + } else { + (void) execve(new_args[0], new_args, new_env); + } + zperror("exec failure"); + return (ENOEXEC); } (void) ct_tmpl_clear(tmpl_fd); @@ -2283,8 +2531,19 @@ main(int argc, char **argv) if (pollerr != 0) { (void) fprintf(stderr, gettext("Error: connection closed due " "to unexpected pollevents=0x%x.\n"), pollerr); - return (1); + return (EPIPE); } - return (0); + /* reap child and get its status */ + if (waitid(P_PID, child_pid, &si, WEXITED | WNOHANG) == -1) { + estatus = errno; + } else if (si.si_pid == 0) { + estatus = ECHILD; + } else if (si.si_code == CLD_EXITED) { + estatus = si.si_status; + } else { + estatus = ECONNABORTED; + } + + return (estatus); } diff --git a/usr/src/cmd/zoneadm/Makefile b/usr/src/cmd/zoneadm/Makefile index 2b01078aec..fba7809c71 100644 --- a/usr/src/cmd/zoneadm/Makefile +++ b/usr/src/cmd/zoneadm/Makefile @@ -21,14 +21,18 @@ # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # PROG= zoneadm +SCRIPTS= MANIFEST= zones.xml resource-mgmt.xml SVCMETHOD= svc-zones svc-resource-mgmt include ../Makefile.cmd +include ../Makefile.ctf +ROOTUSRSBINSCRIPTS= $(SCRIPTS:%=$(ROOTUSRSBIN)/%) ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) OBJS= zoneadm.o zfs.o @@ -42,13 +46,14 @@ CERRWARN += -_gcc=-Wno-uninitialized .KEEP_STATE: -all: $(PROG) +all: $(PROG) $(SCRIPTS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) -install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) +install: all $(ROOTUSRSBINPROG) $(ROOTUSRSBINSCRIPTS) $(ROOTMANIFEST) \ + $(ROOTSVCMETHOD) check: $(PROG).c $(CHKMANIFEST) $(CSTYLE) -pP $(SRCS:%=%) @@ -58,7 +63,7 @@ $(POFILE): $(POFILES) $(CAT) $(POFILES) > $@ clean: - $(RM) $(OBJS) $(POFILES) + $(RM) $(OBJS) $(POFILES) $(SCRIPTS) lint: lint_SRCS diff --git a/usr/src/cmd/zoneadm/svc-zones b/usr/src/cmd/zoneadm/svc-zones index 9d307835bd..30d54f5272 100644 --- a/usr/src/cmd/zoneadm/svc-zones +++ b/usr/src/cmd/zoneadm/svc-zones @@ -32,7 +32,7 @@ shutdown_zones() { zoneadm list -p | nawk -F: '{ - if ($2 != "global") { + if (($5 != "lx") && ($2 != "global")) { print $2 } }' diff --git a/usr/src/cmd/zoneadm/zfs.c b/usr/src/cmd/zoneadm/zfs.c index d27b9c4678..78c165ffd1 100644 --- a/usr/src/cmd/zoneadm/zfs.c +++ b/usr/src/cmd/zoneadm/zfs.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. */ /* @@ -968,6 +968,7 @@ create_zfs_zonepath(char *zonepath) zfs_handle_t *zhp; char zfs_name[MAXPATHLEN]; nvlist_t *props = NULL; + int i; if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK) return; @@ -1004,9 +1005,20 @@ create_zfs_zonepath(char *zonepath) nvlist_free(props); - if (zfs_mount(zhp, NULL, 0) != 0) { + /* + * A monitoring tool might race with us and touch the mountpoint just + * as we're trying to mount, blocking the mount. We wait and retry a + * few times to workaround this race. + */ + for (i = 0; i < 5; i++) { + if (zfs_mount(zhp, NULL, 0) == 0) + break; (void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: " "%s\n"), zfs_name, libzfs_error_description(g_zfs)); + (void) sleep(1); + } + + if (i >= 5) { (void) zfs_destroy(zhp, B_FALSE); } else { if (chmod(zonepath, S_IRWXU) != 0) { diff --git a/usr/src/cmd/zoneadm/zoneadm.c b/usr/src/cmd/zoneadm/zoneadm.c index 6d80fcd8c3..70962d1ea3 100644 --- a/usr/src/cmd/zoneadm/zoneadm.c +++ b/usr/src/cmd/zoneadm/zoneadm.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015, Joyent Inc. All rights reserved. */ /* @@ -100,6 +101,7 @@ typedef struct zone_entry { char zroot[MAXPATHLEN]; char zuuid[UUID_PRINTABLE_STRING_LENGTH]; zone_iptype_t ziptype; + zoneid_t zdid; } zone_entry_t; #define CLUSTER_BRAND_NAME "cluster" @@ -442,6 +444,7 @@ zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) } if (!verbose) { char *cp, *clim; + char zdid[80]; if (!parsable) { (void) printf("%s\n", zent->zname); @@ -457,8 +460,12 @@ zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) (void) printf("%.*s\\:", clim - cp, cp); cp = clim + 1; } - (void) printf("%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand, - ip_type_str); + if (zent->zdid == -1) + zdid[0] = '\0'; + else + (void) snprintf(zdid, sizeof (zdid), "%d", zent->zdid); + (void) printf("%s:%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand, + ip_type_str, zdid); return; } if (zent->zstate_str != NULL) { @@ -553,6 +560,22 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) return (Z_OK); } + if ((handle = zonecfg_init_handle()) == NULL) { + zperror2(zent->zname, gettext("could not init handle")); + return (Z_ERR); + } + if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) { + zperror2(zent->zname, gettext("could not get handle")); + zonecfg_fini_handle(handle); + return (Z_ERR); + } + + if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) { + zperror2(zent->zname, gettext("could not get ip-type")); + zonecfg_fini_handle(handle); + return (Z_ERR); + } + /* * There is a race condition where the zone could boot while * we're walking the index file. In this case the zone state @@ -573,25 +596,11 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) zent->ziptype = ZS_EXCLUSIVE; else zent->ziptype = ZS_SHARED; - return (Z_OK); } } - if ((handle = zonecfg_init_handle()) == NULL) { - zperror2(zent->zname, gettext("could not init handle")); - return (Z_ERR); - } - if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) { - zperror2(zent->zname, gettext("could not get handle")); - zonecfg_fini_handle(handle); - return (Z_ERR); - } + zent->zdid = zonecfg_get_did(handle); - if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) { - zperror2(zent->zname, gettext("could not get ip-type")); - zonecfg_fini_handle(handle); - return (Z_ERR); - } zonecfg_fini_handle(handle); return (Z_OK); @@ -765,18 +774,22 @@ zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable) * Retrieve a zone entry by name. Returns NULL if no such zone exists. */ static zone_entry_t * -lookup_running_zone(const char *str) +lookup_running_zone(const char *name) { - int i; + zoneid_t zid; + zone_entry_t *zent; - if (fetch_zents() != Z_OK) + if ((zid = getzoneidbyname(name)) == -1) return (NULL); - for (i = 0; i < nzents; i++) { - if (strcmp(str, zents[i].zname) == 0) - return (&zents[i]); + if ((zent = malloc(sizeof (zone_entry_t))) == NULL) + return (NULL); + + if (lookup_zone_info(name, zid, zent) != Z_OK) { + free(zent); + return (NULL); } - return (NULL); + return (zent); } /* @@ -1012,8 +1025,12 @@ validate_zonepath(char *path, int cmd_num) (void) printf(gettext("WARNING: %s is on a temporary " "file system.\n"), rpath); } - if (crosscheck_zonepaths(rpath) != Z_OK) - return (Z_ERR); + if (cmd_num != CMD_BOOT && cmd_num != CMD_REBOOT && + cmd_num != CMD_READY) { + /* we checked when we installed, no need to check each boot */ + if (crosscheck_zonepaths(rpath) != Z_OK) + return (Z_ERR); + } /* * Try to collect and report as many minor errors as possible * before returning, so the user can learn everything that needs @@ -1200,6 +1217,7 @@ static int ready_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1208,11 +1226,14 @@ ready_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?")) != EOF) { + if ((arg = getopt(argc, argv, "?X")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_READY, CMD_READY); return (optopt == '?' ? Z_OK : Z_USAGE); + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_READY, CMD_READY); return (Z_USAGE); @@ -1229,6 +1250,7 @@ ready_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_READY; + zarg.debug = debug; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); @@ -1241,6 +1263,7 @@ boot_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; boolean_t force = B_FALSE; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1267,7 +1290,7 @@ boot_func(int argc, char *argv[]) * zoneadm -z myzone boot -- -s -v -m verbose. */ optind = 0; - while ((arg = getopt(argc, argv, "?fs")) != EOF) { + while ((arg = getopt(argc, argv, "?fsX")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_BOOT, CMD_BOOT); @@ -1279,6 +1302,9 @@ boot_func(int argc, char *argv[]) case 'f': force = B_TRUE; break; + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_BOOT, CMD_BOOT); return (Z_USAGE); @@ -1304,6 +1330,7 @@ boot_func(int argc, char *argv[]) if (verify_details(CMD_BOOT, argv) != Z_OK) return (Z_ERR); zarg.cmd = force ? Z_FORCEBOOT : Z_BOOT; + zarg.debug = debug; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); @@ -1819,6 +1846,7 @@ static int halt_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1827,11 +1855,14 @@ halt_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?")) != EOF) { + if ((arg = getopt(argc, argv, "?X")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_HALT, CMD_HALT); return (optopt == '?' ? Z_OK : Z_USAGE); + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_HALT, CMD_HALT); return (Z_USAGE); @@ -1857,6 +1888,7 @@ halt_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_HALT; + zarg.debug = debug; return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0) ? Z_OK : Z_ERR); } @@ -1934,6 +1966,7 @@ static int reboot_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1942,11 +1975,14 @@ reboot_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?")) != EOF) { + if ((arg = getopt(argc, argv, "?X")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_REBOOT, CMD_REBOOT); return (optopt == '?' ? Z_OK : Z_USAGE); + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_REBOOT, CMD_REBOOT); return (Z_USAGE); @@ -1981,6 +2017,7 @@ reboot_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_REBOOT; + zarg.debug = debug; return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0) ? Z_OK : Z_ERR); } @@ -2208,6 +2245,10 @@ verify_fs_special(struct zone_fstab *fstab) if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) return (verify_fs_zfs(fstab)); + if (strcmp(fstab->zone_fs_type, MNTTYPE_HYPRLOFS) == 0 && + strcmp(fstab->zone_fs_special, "swap") == 0) + return (Z_OK); + if (stat64(fstab->zone_fs_special, &st) != 0) { (void) fprintf(stderr, gettext("could not verify fs " "%s: could not access %s: %s\n"), fstab->zone_fs_dir, @@ -2611,7 +2652,6 @@ verify_handle(int cmd_num, zone_dochandle_t handle, char *argv[]) dladm_handle_t dh; dladm_status_t status; datalink_id_t linkid; - char errmsg[DLADM_STRSIZE]; in_alt_root = zonecfg_in_alt_root(); if (in_alt_root) @@ -2694,11 +2734,6 @@ verify_handle(int cmd_num, zone_dochandle_t handle, char *argv[]) dladm_close(dh); } if (status != DLADM_STATUS_OK) { - (void) fprintf(stderr, - gettext("WARNING: skipping network " - "interface '%s': %s\n"), - nwiftab.zone_nwif_physical, - dladm_status2str(status, errmsg)); break; } dl_owner_zid = ALL_ZONES; @@ -2782,6 +2817,61 @@ no_net: return (return_code); } +/* + * Called when readying or booting a zone. We double check that the zone's + * debug ID is set and is unique. This covers the case of pre-existing zones + * with no ID. Also, its possible that a zone was migrated to this host + * and as a result it has a duplicate ID. In this case we preserve the ID + * of the first zone we match on in the index file (since it was there before + * the current zone) and we assign a new unique ID to the current zone. + * Return true if we assigned a new ID, indicating that the zone configuration + * needs to be saved. + */ +static boolean_t +verify_fix_did(zone_dochandle_t handle) +{ + zoneid_t mydid; + zone_entry_t zent; + FILE *cookie; + char *name; + boolean_t fix = B_FALSE; + + mydid = zonecfg_get_did(handle); + if (mydid == -1) { + zonecfg_set_did(handle); + return (B_TRUE); + } + + /* Get the full list of zones from the configuration. */ + cookie = setzoneent(); + while ((name = getzoneent(cookie)) != NULL) { + if (strcmp(target_zone, name) == 0) { + free(name); + break; /* Once we find our entry, stop. */ + } + + if (strcmp(name, "global") == 0 || + lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { + free(name); + continue; + } + + free(name); + if (zent.zdid == mydid) { + fix = B_TRUE; + break; + } + } + endzoneent(cookie); + + if (fix) { + zonecfg_set_did(handle); + return (B_TRUE); + } + + return (B_FALSE); +} + static int verify_details(int cmd_num, char *argv[]) { @@ -2841,6 +2931,18 @@ verify_details(int cmd_num, char *argv[]) if (verify_handle(cmd_num, handle, argv) != Z_OK) return_code = Z_ERR; + if (cmd_num == CMD_READY || cmd_num == CMD_BOOT) { + int vcommit = 0, obscommit = 0; + + vcommit = verify_fix_did(handle); + obscommit = zonecfg_fix_obsolete(handle); + + if (vcommit || obscommit) + if (zonecfg_save(handle) != Z_OK) + (void) fprintf(stderr, gettext("Could not save " + "updated configuration.\n")); + } + zonecfg_fini_handle(handle); if (return_code == Z_ERR) (void) fprintf(stderr, @@ -2926,6 +3028,7 @@ install_func(int argc, char *argv[]) int status; boolean_t do_postinstall = B_FALSE; boolean_t brand_help = B_FALSE; + boolean_t do_dataset = B_TRUE; char opts[128]; if (target_zone == NULL) { @@ -3001,6 +3104,12 @@ install_func(int argc, char *argv[]) } /* Ignore unknown options - may be brand specific. */ break; + case 'x': + if (strcmp(optarg, "nodataset") == 0) { + do_dataset = B_FALSE; + continue; /* internal arg, don't pass thru */ + } + break; default: /* Ignore unknown options - may be brand specific. */ break; @@ -3053,7 +3162,8 @@ install_func(int argc, char *argv[]) goto done; } - create_zfs_zonepath(zonepath); + if (do_dataset) + create_zfs_zonepath(zonepath); } status = do_subproc(cmdbuf); @@ -3863,10 +3973,10 @@ cleanup_zonepath(char *zonepath, boolean_t all) * exist if the zone was force-attached after a * migration. */ - char *std_entries[] = {"dev", "lu", "root", - "SUNWattached.xml", NULL}; - /* (MAXPATHLEN * 3) is for the 3 std_entries dirs */ - char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64]; + char *std_entries[] = {"dev", "lastexited", "logs", "lu", + "root", "SUNWattached.xml", NULL}; + /* (MAXPATHLEN * 5) is for the 5 std_entries dirs */ + char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 5) + 64]; /* * We shouldn't need these checks but lets be paranoid since we @@ -5016,6 +5126,7 @@ uninstall_func(int argc, char *argv[]) if (zonecfg_ping_zoneadmd(target_zone) == Z_OK) { zone_cmd_arg_t zarg; zarg.cmd = Z_NOTE_UNINSTALLING; + zarg.debug = B_FALSE; /* we don't care too much if this fails, just plow on */ (void) zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE); @@ -5131,6 +5242,7 @@ mount_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT; + zarg.debug = B_FALSE; zarg.bootbuf[0] = '\0'; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); @@ -5152,6 +5264,7 @@ unmount_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_UNMOUNT; + zarg.debug = B_FALSE; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); @@ -5373,7 +5486,7 @@ apply_func(int argc, char *argv[]) priv_set_t *privset; zoneid_t zoneid; zone_dochandle_t handle; - struct zone_mcaptab mcap; + uint64_t mcap; char pool_err[128]; zoneid = getzoneid(); @@ -5464,19 +5577,12 @@ apply_func(int argc, char *argv[]) } /* - * If a memory cap is configured, set the cap in the kernel using - * zone_setattr() and make sure the rcapd SMF service is enabled. + * If a memory cap is configured, make sure the rcapd SMF service is + * enabled. */ - if (zonecfg_getmcapent(handle, &mcap) == Z_OK) { - uint64_t num; + if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &mcap) == Z_OK) { char smf_err[128]; - num = (uint64_t)strtoll(mcap.zone_physmem_cap, NULL, 10); - if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { - zerror(gettext("could not set zone memory cap")); - res = Z_ERR; - } - if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) { zerror(gettext("enabling system/rcap service failed: " "%s"), smf_err); diff --git a/usr/src/cmd/zoneadm/zones.xml b/usr/src/cmd/zoneadm/zones.xml index 9c8e305f89..1b6f8cd49b 100644 --- a/usr/src/cmd/zoneadm/zones.xml +++ b/usr/src/cmd/zoneadm/zones.xml @@ -54,11 +54,19 @@ <service_fmri value='svc:/milestone/multi-user-server' /> </dependency> + <dependency + name='metadata' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/smartdc/metadata' /> + </dependency> + <exec_method type='method' name='start' exec='/lib/svc/method/svc-zones %m' - timeout_seconds='60'> + timeout_seconds='0'> </exec_method> <!-- diff --git a/usr/src/cmd/zoneadmd/Makefile b/usr/src/cmd/zoneadmd/Makefile index 8324f7fefa..e81e4631aa 100644 --- a/usr/src/cmd/zoneadmd/Makefile +++ b/usr/src/cmd/zoneadmd/Makefile @@ -18,57 +18,54 @@ # # CDDL HEADER END - -# - # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # PROG= zoneadmd include ../Makefile.cmd +include ../Makefile.ctf -ROOTCMDDIR= $(ROOTLIB)/zones - -OBJS= zoneadmd.o zcons.o vplat.o -SRCS = $(OBJS:.o=.c) -POFILE=zoneadmd_all.po -POFILES= $(OBJS:%.o=%.po) +$(64ONLY)SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) -CFLAGS += $(CCVERBOSE) -CERRWARN += -_gcc=-Wno-switch -CERRWARN += -_gcc=-Wno-parentheses -CERRWARN += -_gcc=-Wno-uninitialized +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint -LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ - -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ - -linetutil -lscf XGETFLAGS += -a -x zoneadmd.xcl +ROOTUSRLIBZONES = $(ROOT)/usr/lib/zones + .KEEP_STATE: .PARALLEL: -all: $(PROG) +all: $(SUBDIRS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) -install: all $(ROOTCMD) - -$(POFILE): $(POFILES) - $(RM) $@ - $(CAT) $(POFILES) > $@ +install: $(SUBDIRS) + -$(RM) $(ROOTUSRLIBZONES)/$(PROG) + -$(LN) $(ISAEXEC) $(ROOTUSRLIBZONES)/$(PROG) -clean: - $(RM) $(OBJS) +$(POFILE): -lint: lint_SRCS +clean clobebr lint: $(SUBDIRS) check: - $(CSTYLE) -p -P $(SRCS:%=%) + $(CSTYLE) -p -P *.c + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: include ../Makefile.targ diff --git a/usr/src/cmd/zoneadmd/Makefile.com b/usr/src/cmd/zoneadmd/Makefile.com new file mode 100644 index 0000000000..c8becc3e8c --- /dev/null +++ b/usr/src/cmd/zoneadmd/Makefile.com @@ -0,0 +1,70 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END + +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014, Joyent, Inc. All rights reserved. +# + +PROG= zoneadmd + +include ../../Makefile.cmd +include ../../Makefile.ctf + +ROOTCMDDIR= $(ROOTLIB)/zones + +OBJS= zoneadmd.o zcons.o zfd.o vplat.o mcap.o + +CFLAGS += $(CCVERBOSE) +LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ + -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ + -linetutil -lproc -lscf + +.KEEP_STATE: + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +ROOTUSRLIBZONES = $(ROOT)/usr/lib/zones +ROOTUSRLIBZONES32 = $(ROOTUSRLIBZONES)/$(MACH32) +ROOTUSRLIBZONES64 = $(ROOTUSRLIBZONES)/$(MACH64) +ROOTUSRLIBZONESPROG32 = $(ROOTUSRLIBZONES32)/$(PROG) +ROOTUSRLIBZONESPROG64 = $(ROOTUSRLIBZONES64)/$(PROG) +$(ROOTUSRLIBZONES32)/%: $(ROOTUSRLIBZONES32) % + $(INS.file) +$(ROOTUSRLIBZONES64)/%: $(ROOTUSRLIBZONES64) % + $(INS.file) +$(ROOTUSRLIBZONES32): + $(INS.dir) + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: + $(RM) $(OBJS) + +lint: + $(LINT.c) ../*.c $(LDLIBS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/zoneadmd/amd64/Makefile b/usr/src/cmd/zoneadmd/amd64/Makefile new file mode 100644 index 0000000000..75ac51db32 --- /dev/null +++ b/usr/src/cmd/zoneadmd/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +.KEEP_STATE: + +include ../Makefile.com +include ../../Makefile.cmd.64 + +install: all $(ROOTUSRLIBZONES64) $(ROOTUSRLIBZONESPROG64) diff --git a/usr/src/cmd/zoneadmd/i386/Makefile b/usr/src/cmd/zoneadmd/i386/Makefile new file mode 100644 index 0000000000..a8764e0638 --- /dev/null +++ b/usr/src/cmd/zoneadmd/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +.KEEP_STATE: + +include ../Makefile.com + +install: all $(ROOTUSRLIBZONES32) $(ROOTUSRLIBZONESPROG32) diff --git a/usr/src/cmd/zoneadmd/mcap.c b/usr/src/cmd/zoneadmd/mcap.c new file mode 100644 index 0000000000..16cd2dd07a --- /dev/null +++ b/usr/src/cmd/zoneadmd/mcap.c @@ -0,0 +1,1182 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2014, Joyent, Inc. All rights reserved. + */ + +/* + * This file implements the code which runs a thread inside zoneadmd to cap + * the associated zone's physical memory. A thread to do this is started + * when the zone boots and is halted when the zone shuts down. + * + * Because of the way that the VM system is currently implemented, there is no + * way to go from the bottom up (page to process to zone). Thus, there is no + * obvious way to hook an rctl into the kernel's paging code to enforce a hard + * memory cap. Instead, we implement a soft physical memory cap which looks + * at the zone's overall rss and once it is over the cap, works from the top + * down (zone to process to page), looking at zone processes, to determine + * what to try to pageout to get the zone under its memory cap. + * + * The code uses the fast, cheap, but potentially very inaccurate sum of the + * rss values from psinfo_t to first approximate the zone's rss and will + * fallback to the vm_getusage syscall to determine the zone's rss if needed. + * It then checks the rss against the zone's zone.max-physical-memory rctl. + * Once the zone goes over its cap, then this thread will work through the + * zone's /proc process list, Pgrab-bing each process and stepping through the + * address space segments attempting to use pr_memcntl(...MS_INVALCURPROC...) + * to pageout pages, until the zone is again under its cap. + * + * Although zone memory capping is implemented as a soft cap by this user-level + * thread, the interfaces around memory caps that are exposed to the user are + * the standard ones; an rctl and kstats. This thread uses the rctl value + * to obtain the cap and works with the zone kernel code to update the kstats. + * If the implementation ever moves into the kernel, these exposed interfaces + * do not need to change. + * + * The thread adaptively sleeps, periodically checking the state of the + * zone. As the zone's rss gets closer to the cap, the thread will wake up + * more often to check the zone's status. Once the zone is over the cap, + * the thread will work to pageout until the zone is under the cap, as shown + * by updated vm_usage data. + * + * NOTE: The pagedata page maps (at least on x86) are not useful. Those flags + * are set by hrm_setbits() and on x86 that code path is only executed by + * segvn_pagelock -> hat_setstat -> hrm_setbits + * segvn_softunlock -^ + * On SPARC there is an additional code path which may make this data + * useful (sfmmu_ttesync), but since it is not generic, we ignore the page + * maps. If we ever fix this issue, then we could generalize this mcap code to + * do more with the data on active pages. + * + * For debugging, touch the file {zonepath}/mcap_debug.log. This will + * cause the thread to start logging its actions into that file (it may take + * a minute or two if the thread is currently sleeping). Removing that + * file will cause logging to stop. + */ + +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <libproc.h> +#include <limits.h> +#include <procfs.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <time.h> +#include <unistd.h> +#include <sys/priocntl.h> +#include <dirent.h> +#include <zone.h> +#include <libzonecfg.h> +#include <thread.h> +#include <values.h> +#include <sys/vm_usage.h> +#include <sys/resource.h> +#include <sys/debug.h> +#include <synch.h> +#include <wait.h> +#include <libcontract.h> +#include <libcontract_priv.h> +#include <sys/contract/process.h> +#include "zoneadmd.h" + + /* round up to next y = 2^n */ +#define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) + +#define CAP_REFRESH ((uint64_t)300 * NANOSEC) /* every 5 minutes */ + +/* + * zonecfg attribute tunables for memory capping. + * phys-mcap-cmd + * type: string + * specifies a command that can be run when over the cap + * phys-mcap-no-vmusage + * type: boolean + * true disables vm_getusage and just uses zone's proc. rss sum + * phys-mcap-no-pageout + * type: boolean + * true disables pageout when over + * phys-mcap-no-pf-throttle + * type: boolean + * true disables page fault throttling when over + */ +#define TUNE_CMD "phys-mcap-cmd" +#define TUNE_NVMU "phys-mcap-no-vmusage" +#define TUNE_NPAGE "phys-mcap-no-pageout" +#define TUNE_NPFTHROT "phys-mcap-no-pf-throttle" + +/* + * These are only used in get_mem_info but global. We always need scale_rss and + * prev_fast_rss to be persistent but we also have the other two global so we + * can easily see these with mdb. + */ +uint64_t scale_rss = 0; +uint64_t prev_fast_rss = 0; +uint64_t fast_rss = 0; +uint64_t accurate_rss = 0; + +static char zoneproc[MAXPATHLEN]; +static char debug_log[MAXPATHLEN]; +static zoneid_t zid; +static mutex_t shutdown_mx; +static cond_t shutdown_cv; +static int shutting_down = 0; +static thread_t mcap_tid; +static FILE *debug_log_fp = NULL; +static uint64_t zone_rss_cap; /* RSS cap(KB) */ +static char over_cmd[2 * BUFSIZ]; /* same size as zone_attr_value */ +static boolean_t skip_vmusage = B_FALSE; +static boolean_t skip_pageout = B_FALSE; +static boolean_t skip_pf_throttle = B_FALSE; + +static zlog_t *logp; + +static int64_t check_suspend(); +static void get_mcap_tunables(); + +/* + * Structure to hold current state about a process address space that we're + * working on. + */ +typedef struct { + int pr_curr; /* the # of the mapping we're working on */ + int pr_nmap; /* number of mappings in address space */ + prmap_t *pr_mapp; /* process's map array */ +} proc_map_t; + +typedef struct zsd_vmusage64 { + id_t vmu_zoneid; + uint_t vmu_type; + id_t vmu_id; + /* + * An amd64 kernel will align the following uint64_t members, but a + * 32bit i386 process will not without help. + */ + int vmu_align_next_members_on_8_bytes; + uint64_t vmu_rss_all; + uint64_t vmu_rss_private; + uint64_t vmu_rss_shared; + uint64_t vmu_swap_all; + uint64_t vmu_swap_private; + uint64_t vmu_swap_shared; +} zsd_vmusage64_t; + +/* + * Output a debug log message. + */ +/*PRINTFLIKE1*/ +static void +debug(char *fmt, ...) +{ + va_list ap; + + if (debug_log_fp == NULL) + return; + + va_start(ap, fmt); + (void) vfprintf(debug_log_fp, fmt, ap); + va_end(ap); + (void) fflush(debug_log_fp); +} + +/* + * Like sleep(3C) but can be interupted by cond_signal which is posted when + * we're shutting down the mcap thread. + */ +static void +sleep_shutdown(int secs) +{ + timestruc_t to; + + to.tv_sec = secs; + to.tv_nsec = 0; + + (void) mutex_lock(&shutdown_mx); + if (!shutting_down) + (void) cond_reltimedwait(&shutdown_cv, &shutdown_mx, &to); + (void) mutex_unlock(&shutdown_mx); +} + +static boolean_t +proc_issystem(pid_t pid) +{ + char pc_clname[PC_CLNMSZ]; + + if (priocntl(P_PID, pid, PC_GETXPARMS, NULL, PC_KY_CLNAME, pc_clname, + PC_KY_NULL) != -1) + return (strcmp(pc_clname, "SYS") == 0); + + return (B_TRUE); +} + +/* + * Fork a child that enters the zone and runs the "phys-mcap-cmd" command. + */ +static void +run_over_cmd() +{ + int ctfd; + int err; + pid_t childpid; + siginfo_t info; + ctid_t ct; + + /* + * Before we enter the zone, we need to create a new process contract + * for the child, as required by zone_enter(). + */ + if ((ctfd = open64("/system/contract/process/template", O_RDWR)) == -1) + return; + if (ct_tmpl_set_critical(ctfd, 0) != 0 || + ct_tmpl_set_informative(ctfd, 0) != 0 || + ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR) != 0 || + ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY) != 0 || + ct_tmpl_activate(ctfd) != 0) { + (void) close(ctfd); + return; + } + + childpid = fork(); + switch (childpid) { + case -1: + (void) ct_tmpl_clear(ctfd); + (void) close(ctfd); + break; + case 0: /* Child */ + (void) ct_tmpl_clear(ctfd); + (void) close(ctfd); + if (zone_enter(zid) == -1) + _exit(errno); + err = system(over_cmd); + _exit(err); + break; + default: /* Parent */ + if (contract_latest(&ct) == -1) + ct = -1; + (void) ct_tmpl_clear(ctfd); + (void) close(ctfd); + err = waitid(P_PID, childpid, &info, WEXITED); + (void) contract_abandon_id(ct); + if (err == -1 || info.si_status != 0) + debug("over_cmd failed"); + break; + } +} + +/* + * Get the next mapping. + */ +static prmap_t * +nextmapping(proc_map_t *pmp) +{ + if (pmp->pr_mapp == NULL || pmp->pr_curr >= pmp->pr_nmap) + return (NULL); + + return (&pmp->pr_mapp[pmp->pr_curr++]); +} + +/* + * Initialize the proc_map_t to access the first mapping of an address space. + */ +static prmap_t * +init_map(proc_map_t *pmp, pid_t pid) +{ + int fd; + int res; + struct stat st; + char pathbuf[MAXPATHLEN]; + + bzero(pmp, sizeof (proc_map_t)); + pmp->pr_nmap = -1; + + (void) snprintf(pathbuf, sizeof (pathbuf), "%s/%d/map", zoneproc, pid); + if ((fd = open(pathbuf, O_RDONLY, 0)) < 0) + return (NULL); + +redo: + errno = 0; + if (fstat(fd, &st) != 0) + goto done; + + if ((pmp->pr_mapp = malloc(st.st_size)) == NULL) { + debug("cannot malloc() %ld bytes for xmap", st.st_size); + goto done; + } + (void) bzero(pmp->pr_mapp, st.st_size); + + errno = 0; + if ((res = pread(fd, pmp->pr_mapp, st.st_size, 0)) != st.st_size) { + free(pmp->pr_mapp); + pmp->pr_mapp = NULL; + if (res > 0 || errno == E2BIG) { + goto redo; + } else { + debug("pid %ld cannot read xmap\n", pid); + goto done; + } + } + + pmp->pr_nmap = st.st_size / sizeof (prmap_t); + +done: + (void) close(fd); + return (nextmapping(pmp)); +} + +/* + * Attempt to invalidate the entire mapping from within the given process's + * address space. May return nonzero with errno as: + * ESRCH - process not found + * ENOMEM - segment not found + * EINVAL - mapping exceeds a single segment + */ +static int +pageout_mapping(pid_t pid, prmap_t *pmp) +{ + int res; + + if (pmp->pr_mflags & MA_ISM || pmp->pr_mflags & MA_SHM) + return (0); + + errno = 0; + res = syscall(SYS_rusagesys, _RUSAGESYS_INVALMAP, pid, pmp->pr_vaddr, + pmp->pr_size); + + return (res); +} + +/* + * Work through a process paging out mappings until the whole address space was + * examined or the excess is < 0. Return our estimate of the updated excess. + */ +static int64_t +pageout_process(pid_t pid, int64_t excess) +{ + int psfd; + prmap_t *pmap; + proc_map_t cur; + int res; + int64_t sum_d_rss, d_rss; + int64_t old_rss; + int map_cnt; + psinfo_t psinfo; + char pathbuf[MAXPATHLEN]; + + (void) snprintf(pathbuf, sizeof (pathbuf), "%s/%d/psinfo", zoneproc, + pid); + if ((psfd = open(pathbuf, O_RDONLY, 0000)) < 0) + return (excess); + + cur.pr_mapp = NULL; + + if (pread(psfd, &psinfo, sizeof (psinfo), 0) != sizeof (psinfo)) + goto done; + + old_rss = (int64_t)psinfo.pr_rssize; + map_cnt = 0; + + /* If unscannable, skip it. */ + if (psinfo.pr_nlwp == 0 || proc_issystem(pid)) { + debug("pid %ld: system process, skipping %s\n", + pid, psinfo.pr_psargs); + goto done; + } + + /* If tiny RSS (16KB), skip it. */ + if (old_rss <= 16) { + debug("pid %ld: skipping, RSS %lldKB %s\n", + pid, old_rss, psinfo.pr_psargs); + goto done; + } + + /* Get segment residency information. */ + pmap = init_map(&cur, pid); + + /* Skip process if it has no mappings. */ + if (pmap == NULL) { + debug("pid %ld: map unreadable; ignoring\n", pid); + goto done; + } + + debug("pid %ld: nmap %d sz %dKB rss %lldKB %s\n", + pid, cur.pr_nmap, psinfo.pr_size, old_rss, psinfo.pr_psargs); + + /* + * Within the process's address space, attempt to page out mappings. + */ + sum_d_rss = 0; + while (excess > 0 && pmap != NULL && !shutting_down) { + /* invalidate the entire mapping */ + if ((res = pageout_mapping(pid, pmap)) < 0) + debug("pid %ld: mapping 0x%p %ldkb unpageable (%d)\n", + pid, pmap->pr_vaddr, pmap->pr_size / 1024, errno); + + map_cnt++; + + /* + * Re-check the process rss and get the delta. + */ + if (pread(psfd, &psinfo, sizeof (psinfo), 0) + != sizeof (psinfo)) { + excess -= old_rss; + goto done; + } + + d_rss = (int64_t)psinfo.pr_rssize - old_rss; + old_rss = (int64_t)psinfo.pr_rssize; + sum_d_rss += d_rss; + + /* + * d_rss hopefully should be negative (or 0 if nothing + * invalidated) but can be positive if more got paged in. + */ + excess += d_rss; + + if (excess <= 0) { + debug("pid %ld: (part.) nmap %d delta_rss %lldKB " + "excess %lldKB\n", pid, map_cnt, + (unsigned long long)sum_d_rss, (long long)excess); + map_cnt = 0; + + /* + * If we're actually under, this will suspend checking + * in the middle of this process's address space. + */ + excess = check_suspend(); + if (shutting_down) + goto done; + + /* + * since we might have suspended, re-read process's rss + */ + if (pread(psfd, &psinfo, sizeof (psinfo), 0) + != sizeof (psinfo)) { + excess -= old_rss; + goto done; + } + + old_rss = (int64_t)psinfo.pr_rssize; + + debug("pid %ld: resume pageout; excess %lld\n", pid, + (long long)excess); + sum_d_rss = 0; + } + + pmap = nextmapping(&cur); + } + + debug("pid %ld: nmap %d delta_rss %lldKB excess %lldKB\n", + pid, map_cnt, (unsigned long long)sum_d_rss, (long long)excess); + +done: + if (cur.pr_mapp != NULL) + free(cur.pr_mapp); + + (void) close(psfd); + + if (shutting_down) + return (0); + + return (excess); +} + +/* + * Get the zone's RSS data. + */ +static uint64_t +get_mem_info() +{ + uint64_t n = 1; + zsd_vmusage64_t buf; + uint64_t tmp_rss; + DIR *pdir = NULL; + struct dirent *dent; + + /* + * Start by doing the fast, cheap RSS calculation using the rss value + * in psinfo_t. Because that's per-process, it can lead to double + * counting some memory and overestimating how much is being used, but + * as long as that's not over the cap, then we don't need do the + * expensive calculation. + * + * If we have to do the expensive calculation, we remember the scaling + * factor so that we can try to use that on subsequent iterations for + * the fast rss. + */ + if (shutting_down) + return (0); + + if ((pdir = opendir(zoneproc)) == NULL) + return (0); + + accurate_rss = 0; + fast_rss = 0; + while (!shutting_down && (dent = readdir(pdir)) != NULL) { + pid_t pid; + int psfd; + int64_t rss; + char pathbuf[MAXPATHLEN]; + psinfo_t psinfo; + + if (strcmp(".", dent->d_name) == 0 || + strcmp("..", dent->d_name) == 0) + continue; + + pid = atoi(dent->d_name); + if (pid == 0 || pid == 1) + continue; + + (void) snprintf(pathbuf, sizeof (pathbuf), "%s/%d/psinfo", + zoneproc, pid); + + rss = 0; + if ((psfd = open(pathbuf, O_RDONLY, 0000)) != -1) { + if (pread(psfd, &psinfo, sizeof (psinfo), 0) == + sizeof (psinfo)) + rss = (int64_t)psinfo.pr_rssize; + + (void) close(psfd); + } + + fast_rss += rss; + } + + (void) closedir(pdir); + + if (shutting_down) + return (0); + + debug("fast rss: %lluKB, scale: %llu, prev: %lluKB\n", fast_rss, + scale_rss, prev_fast_rss); + + /* see if we can get by with a scaled fast rss */ + tmp_rss = fast_rss; + if (scale_rss > 1 && prev_fast_rss > 0) { + /* + * Only scale the fast value if it hasn't ballooned too much + * to trust. + */ + if (fast_rss / prev_fast_rss < 2) { + fast_rss /= scale_rss; + debug("scaled fast rss: %lluKB\n", fast_rss); + } + } + + if (fast_rss <= zone_rss_cap || skip_vmusage) { + uint64_t zone_rss_bytes; + + zone_rss_bytes = fast_rss * 1024; + /* Use the zone's approx. RSS in the kernel */ + (void) zone_setattr(zid, ZONE_ATTR_RSS, &zone_rss_bytes, 0); + return (fast_rss); + } + + buf.vmu_id = zid; + + /* get accurate usage (cached data may be up to 5 seconds old) */ + if (syscall(SYS_rusagesys, _RUSAGESYS_GETVMUSAGE, VMUSAGE_A_ZONE, 5, + (uintptr_t)&buf, (uintptr_t)&n) != 0) { + debug("vmusage failed\n"); + (void) sleep_shutdown(1); + return (0); + } + + if (n > 1) { + /* This should never happen */ + debug("vmusage returned more than one result\n"); + (void) sleep_shutdown(1); + return (0); + } + + if (buf.vmu_id != zid) { + /* This should never happen */ + debug("vmusage returned the incorrect zone\n"); + (void) sleep_shutdown(1); + return (0); + } + + accurate_rss = buf.vmu_rss_all / 1024; + + /* calculate scaling factor to use for fast_rss from now on */ + if (accurate_rss > 0) { + scale_rss = fast_rss / accurate_rss; + debug("new scaling factor: %llu\n", scale_rss); + /* remember the fast rss when we had to get the accurate rss */ + prev_fast_rss = tmp_rss; + } + + debug("accurate rss: %lluKB, scale: %llu, prev: %lluKB\n", accurate_rss, + scale_rss, prev_fast_rss); + return (accurate_rss); +} + +/* + * Needed to read the zones physical-memory-cap rctl. + */ +static struct ps_prochandle * +grab_zone_proc() +{ + DIR *dirp; + struct dirent *dentp; + struct ps_prochandle *ph = NULL; + int tmp; + + if ((dirp = opendir(zoneproc)) == NULL) + return (NULL); + + while (!shutting_down && (dentp = readdir(dirp))) { + int pid; + + if (strcmp(".", dentp->d_name) == 0 || + strcmp("..", dentp->d_name) == 0) + continue; + + pid = atoi(dentp->d_name); + /* attempt to grab process */ + if ((ph = Pgrab(pid, 0, &tmp)) != NULL) { + if (Psetflags(ph, PR_RLC) == 0) { + if (Pcreate_agent(ph) == 0) { + (void) closedir(dirp); + return (ph); + } + } + Prelease(ph, 0); + } + } + + (void) closedir(dirp); + return (NULL); +} + +static uint64_t +get_zone_cap() +{ + rctlblk_t *rblk; + uint64_t mcap; + struct ps_prochandle *ph; + + if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) + return (UINT64_MAX); + + if ((ph = grab_zone_proc()) == NULL) { + free(rblk); + return (UINT64_MAX); + } + + if (pr_getrctl(ph, "zone.max-physical-memory", NULL, rblk, + RCTL_FIRST)) { + Pdestroy_agent(ph); + Prelease(ph, 0); + free(rblk); + return (UINT64_MAX); + } + + Pdestroy_agent(ph); + Prelease(ph, 0); + + mcap = rctlblk_get_value(rblk); + free(rblk); + return (mcap); +} + +/* + * check_suspend is invoked at the beginning of every pass through the process + * list or after we've paged out enough so that we think the excess is under + * the cap. The purpose is to periodically check the zone's rss and return + * the excess when the zone is over the cap. The rest of the time this + * function will sleep, periodically waking up to check the current rss. + * + * Depending on the percentage of penetration of the zone's rss into the + * cap we sleep for longer or shorter amounts. This reduces the impact of this + * work on the system, which is important considering that each zone will be + * monitoring its rss. + */ +static int64_t +check_suspend() +{ + static hrtime_t last_cap_read = 0; + static uint64_t addon; + static uint64_t lo_thresh; /* Thresholds for how long to sleep */ + static uint64_t hi_thresh; /* when under the cap (80% & 90%). */ + static uint64_t prev_zone_rss = 0; + static uint32_t pfdelay = 0; /* usec page fault delay when over */ + + /* Wait a second to give the async pageout a chance to catch up. */ + (void) sleep_shutdown(1); + + while (!shutting_down) { + int64_t new_excess; + int sleep_time; + hrtime_t now; + struct stat st; + uint64_t zone_rss; /* total RSS(KB) */ + + /* + * Check if the debug log files exists and enable or disable + * debug. + */ + if (debug_log_fp == NULL) { + if (stat(debug_log, &st) == 0) + debug_log_fp = fopen(debug_log, "w"); + } else { + if (stat(debug_log, &st) == -1) { + (void) fclose(debug_log_fp); + debug_log_fp = NULL; + } + } + + /* + * If the CAP_REFRESH interval has passed, re-get the current + * cap in case it has been dynamically updated. + */ + now = gethrtime(); + if (now - last_cap_read > CAP_REFRESH) { + uint64_t mcap; + + last_cap_read = now; + + mcap = get_zone_cap(); + if (mcap != 0 && mcap != UINT64_MAX) + zone_rss_cap = ROUNDUP(mcap, 1024) / 1024; + else + zone_rss_cap = UINT64_MAX; + + lo_thresh = (uint64_t)(zone_rss_cap * .8); + hi_thresh = (uint64_t)(zone_rss_cap * .9); + addon = (uint64_t)(zone_rss_cap * 0.05); + + /* + * We allow the memory cap tunables to be changed on + * the fly. + */ + get_mcap_tunables(); + + debug("%s: %s\n", TUNE_CMD, over_cmd); + debug("%s: %d\n", TUNE_NVMU, skip_vmusage); + debug("%s: %d\n", TUNE_NPAGE, skip_pageout); + debug("%s: %d\n", TUNE_NPFTHROT, skip_pf_throttle); + debug("current cap %lluKB lo %lluKB hi %lluKB\n", + zone_rss_cap, lo_thresh, hi_thresh); + } + + /* No cap, nothing to do. */ + if (zone_rss_cap == 0 || zone_rss_cap == UINT64_MAX) { + debug("no cap, sleep 120 seconds\n"); + (void) sleep_shutdown(120); + continue; + } + + zone_rss = get_mem_info(); + + /* calculate excess */ + new_excess = zone_rss - zone_rss_cap; + + debug("rss %lluKB, cap %lluKB, excess %lldKB\n", + zone_rss, zone_rss_cap, new_excess); + + /* + * If necessary, updates stats. + */ + + /* + * If it looks like we did some paging out since last over the + * cap then update the kstat so we can approximate how much was + * paged out. + */ + if (prev_zone_rss > zone_rss_cap && zone_rss < prev_zone_rss) { + uint64_t diff; + + /* assume diff is num bytes we paged out */ + diff = (prev_zone_rss - zone_rss) * 1024; + + (void) zone_setattr(zid, ZONE_ATTR_PMCAP_PAGEOUT, + &diff, 0); + } + prev_zone_rss = zone_rss; + + if (new_excess > 0) { + uint64_t n = 1; + + /* Increment "nover" kstat. */ + (void) zone_setattr(zid, ZONE_ATTR_PMCAP_NOVER, &n, 0); + + if (!skip_pf_throttle) { + /* + * Tell the kernel to start throttling page + * faults by some number of usecs to help us + * catch up. If we are persistently over the + * cap the delay ramps up to a max of 2000usecs. + * Note that for delays less than 1 tick + * (i.e. all of these) we busy-wait in as_fault. + * delay faults/sec + * 125 8000 + * 250 4000 + * 500 2000 + * 1000 1000 + * 2000 500 + */ + if (pfdelay == 0) + pfdelay = 125; + else if (pfdelay < 2000) + pfdelay *= 2; + + (void) zone_setattr(zid, ZONE_ATTR_PG_FLT_DELAY, + &pfdelay, 0); + } + + /* + * Once we go over the cap, then we want to + * page out a little extra instead of stopping + * right at the cap. To do this we add 5% to + * the excess so that pageout_proces will work + * a little longer before stopping. + */ + return ((int64_t)(new_excess + addon)); + } + + /* + * At this point we are under the cap. + * + * Tell the kernel to stop throttling page faults. + * + * Scale the amount of time we sleep before rechecking the + * zone's memory usage. Also, scale the accpetable age of + * cached results from vm_getusage. We do this based on the + * penetration into the capped limit. + */ + if (pfdelay > 0) { + pfdelay = 0; + (void) zone_setattr(zid, ZONE_ATTR_PG_FLT_DELAY, + &pfdelay, 0); + } + + if (zone_rss <= lo_thresh) { + sleep_time = 120; + } else if (zone_rss <= hi_thresh) { + sleep_time = 60; + } else { + sleep_time = 30; + } + + debug("sleep %d seconds\n", sleep_time); + (void) sleep_shutdown(sleep_time); + } + + /* Shutting down, tell the kernel so it doesn't throttle */ + if (pfdelay > 0) { + pfdelay = 0; + (void) zone_setattr(zid, ZONE_ATTR_PG_FLT_DELAY, &pfdelay, 0); + } + + return (0); +} + +static void +get_mcap_tunables() +{ + zone_dochandle_t handle; + struct zone_attrtab attr; + + over_cmd[0] = '\0'; + if ((handle = zonecfg_init_handle()) == NULL) + return; + + if (zonecfg_get_handle(zone_name, handle) != Z_OK) + goto done; + + /* Reset to defaults in case rebooting and settings have changed */ + over_cmd[0] = '\0'; + skip_vmusage = B_FALSE; + skip_pageout = B_FALSE; + skip_pf_throttle = B_FALSE; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp(TUNE_CMD, attr.zone_attr_name) == 0) { + (void) strlcpy(over_cmd, attr.zone_attr_value, + sizeof (over_cmd)); + } else if (strcmp(TUNE_NVMU, attr.zone_attr_name) == 0) { + if (strcmp("true", attr.zone_attr_value) == 0) + skip_vmusage = B_TRUE; + } else if (strcmp(TUNE_NPAGE, attr.zone_attr_name) == 0) { + if (strcmp("true", attr.zone_attr_value) == 0) + skip_pageout = B_TRUE; + } else if (strcmp(TUNE_NPFTHROT, attr.zone_attr_name) == 0) { + if (strcmp("true", attr.zone_attr_value) == 0) + skip_pf_throttle = B_TRUE; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); +} + +/* ARGSUSED */ +static int +chk_proc_fs(void *data, const char *spec, const char *dir, + const char *fstype, const char *opt) +{ + if (fstype != NULL && strcmp(fstype, "proc") == 0) + *((boolean_t *)data) = B_TRUE; + + return (0); +} + +static boolean_t +has_proc() +{ + brand_handle_t bh; + boolean_t fnd = B_FALSE; + + if ((bh = brand_open(brand_name)) != NULL) { + (void) brand_platform_iter_mounts(bh, chk_proc_fs, &fnd); + } + + brand_close(bh); + return (fnd); +} + +/* + * We run this loop for brands with no /proc to simply update the RSS, using + * the cheap GZ /proc data, every 5 minutes. + */ +static void +no_procfs() +{ + DIR *pdir = NULL; + struct dirent *dent; + uint64_t zone_rss_bytes; + + (void) sleep_shutdown(30); + while (!shutting_down) { + /* + * Just do the fast, cheap RSS calculation using the rss value + * in psinfo_t. Because that's per-process, it can lead to + * double counting some memory and overestimating how much is + * being used. Since there is no /proc in the zone, we use the + * GZ /proc and check for the correct zone. + */ + if ((pdir = opendir("/proc")) == NULL) + return; + + fast_rss = 0; + while (!shutting_down && (dent = readdir(pdir)) != NULL) { + pid_t pid; + int psfd; + int64_t rss; + char pathbuf[MAXPATHLEN]; + psinfo_t psinfo; + + if (strcmp(".", dent->d_name) == 0 || + strcmp("..", dent->d_name) == 0) + continue; + + pid = atoi(dent->d_name); + if (pid == 0 || pid == 1) + continue; + + (void) snprintf(pathbuf, sizeof (pathbuf), + "/proc/%d/psinfo", pid); + + rss = 0; + if ((psfd = open(pathbuf, O_RDONLY, 0000)) != -1) { + if (pread(psfd, &psinfo, sizeof (psinfo), 0) == + sizeof (psinfo)) { + if (psinfo.pr_zoneid == zid) + rss = (int64_t)psinfo.pr_rssize; + } + + (void) close(psfd); + } + + fast_rss += rss; + } + + (void) closedir(pdir); + + if (shutting_down) + return; + + zone_rss_bytes = fast_rss * 1024; + /* Use the zone's approx. RSS in the kernel */ + (void) zone_setattr(zid, ZONE_ATTR_RSS, &zone_rss_bytes, 0); + + (void) sleep_shutdown(300); + } +} + +/* + * Thread that checks zone's memory usage and when over the cap, goes through + * the zone's process list trying to pageout processes to get under the cap. + */ +static void +mcap_zone() +{ + DIR *pdir = NULL; + int64_t excess; + + debug("thread startup\n"); + + get_mcap_tunables(); + + /* + * If the zone has no /proc filesystem, we can't use the fast algorithm + * to check RSS or pageout any processes. All we can do is periodically + * update it's RSS kstat using the expensive sycall. + */ + if (!has_proc()) { + no_procfs(); + debug("thread shutdown\n"); + return; + } + + /* + * When first starting it is likely lots of other zones are starting + * too because the system is booting. Since we just started the zone + * we're not worried about being over the cap right away, so we let + * things settle a bit and tolerate some older data here to minimize + * the load on the system. + */ + (void) sleep_shutdown(15); /* wait 15 secs. so the zone can get going */ + + /* Wait until zone's /proc is mounted */ + while (!shutting_down) { + struct stat st; + + if (stat(zoneproc, &st) == 0 && + strcmp(st.st_fstype, "proc") == 0) + break; + sleep_shutdown(5); + } + + /* Open zone's /proc and walk entries. */ + while (!shutting_down) { + if ((pdir = opendir(zoneproc)) != NULL) + break; + sleep_shutdown(5); + } + + while (!shutting_down) { + struct dirent *dirent; + + /* Wait until we've gone over the cap. */ + excess = check_suspend(); + + debug("starting to scan, excess %lldk\n", (long long)excess); + + if (over_cmd[0] != '\0') { + uint64_t zone_rss; /* total RSS(KB) */ + + debug("run phys_mcap_cmd: %s\n", over_cmd); + run_over_cmd(); + + zone_rss = get_mem_info(); + excess = zone_rss - zone_rss_cap; + debug("rss %lluKB, cap %lluKB, excess %lldKB\n", + zone_rss, zone_rss_cap, excess); + if (excess <= 0) + continue; + } + + while (!shutting_down && (dirent = readdir(pdir)) != NULL) { + pid_t pid; + + if (strcmp(".", dirent->d_name) == 0 || + strcmp("..", dirent->d_name) == 0) + continue; + + pid = atoi(dirent->d_name); + if (pid == 0 || pid == 1) + continue; + + if (skip_pageout) + (void) sleep_shutdown(2); + else + excess = pageout_process(pid, excess); + + if (excess <= 0) { + debug("apparently under; excess %lld\n", + (long long)excess); + /* Double check the current excess */ + excess = check_suspend(); + } + } + + debug("process pass done; excess %lld\n", (long long)excess); + rewinddir(pdir); + + if (skip_pageout) + (void) sleep_shutdown(120); + } + + if (pdir != NULL) + (void) closedir(pdir); + debug("thread shutdown\n"); +} + +void +create_mcap_thread(zlog_t *zlogp, zoneid_t id) +{ + int res; + + shutting_down = 0; + zid = id; + logp = zlogp; + + /* all but the lx brand currently use /proc */ + if (strcmp(brand_name, "lx") == 0) { + (void) snprintf(zoneproc, sizeof (zoneproc), + "%s/root/native/proc", zonepath); + } else { + (void) snprintf(zoneproc, sizeof (zoneproc), "%s/root/proc", + zonepath); + } + + (void) snprintf(debug_log, sizeof (debug_log), "%s/mcap_debug.log", + zonepath); + + res = thr_create(NULL, NULL, (void *(*)(void *))mcap_zone, NULL, NULL, + &mcap_tid); + if (res != 0) { + zerror(zlogp, B_FALSE, "error %d creating memory cap thread", + res); + mcap_tid = 0; + } +} + +void +destroy_mcap_thread() +{ + if (mcap_tid != 0) { + shutting_down = 1; + (void) cond_signal(&shutdown_cv); + (void) thr_join(mcap_tid, NULL, NULL); + mcap_tid = 0; + } +} diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index b9954b81b3..522de5b779 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent Inc. All rights reserved. + * Copyright 2015, Joyent Inc. All rights reserved. */ /* @@ -137,6 +137,9 @@ #define ALT_MOUNT(mount_cmd) ((mount_cmd) != Z_MNT_BOOT) +/* Number of times to retry unmounting if it fails */ +#define UMOUNT_RETRIES 30 + /* a reasonable estimate for the number of lwps per process */ #define LWPS_PER_PROCESS 10 @@ -160,11 +163,25 @@ static priv_set_t *zprivs = NULL; static const char *DFLT_FS_ALLOWED = "hsfs,smbfs,nfs,nfs3,nfs4,nfsdyn"; +typedef struct zone_proj_rctl_map { + char *zpr_zone_rctl; + char *zpr_project_rctl; +} zone_proj_rctl_map_t; + +static zone_proj_rctl_map_t zone_proj_rctl_map[] = { + {"zone.max-msg-ids", "project.max-msg-ids"}, + {"zone.max-sem-ids", "project.max-sem-ids"}, + {"zone.max-shm-ids", "project.max-shm-ids"}, + {"zone.max-shm-memory", "project.max-shm-memory"}, + {NULL, NULL} +}; + /* from libsocket, not in any header file */ extern int getnetmaskbyaddr(struct in_addr, struct in_addr *); /* from zoneadmd */ extern char query_hook[]; +extern char post_statechg_hook[]; /* * For each "net" resource configured in zonecfg, we track a zone_addr_list_t @@ -201,7 +218,7 @@ autofs_cleanup(zoneid_t zoneid) /* * Ask autofs to unmount all trigger nodes in the given zone. */ - return (_autofssys(AUTOFS_UNMOUNTALL, (void *)zoneid)); + return (_autofssys(AUTOFS_UNMOUNTALL, (void *)((uintptr_t)zoneid))); } static void @@ -592,6 +609,24 @@ root_to_lu(zlog_t *zlogp, char *zroot, size_t zrootlen, boolean_t isresolved) } /* + * Perform brand-specific cleanup if we are unable to unmount a FS. + */ +static void +brand_umount_cleanup(zlog_t *zlogp, char *path) +{ + char cmdbuf[2 * MAXPATHLEN]; + + if (post_statechg_hook[0] == '\0') + return; + + if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %d %d %s", post_statechg_hook, + ZONE_STATE_DOWN, Z_UNMOUNT, path) > sizeof (cmdbuf)) + return; + + (void) do_subproc(zlogp, cmdbuf, NULL, B_FALSE); +} + +/* * The general strategy for unmounting filesystems is as follows: * * - Remote filesystems may be dead, and attempting to contact them as @@ -624,6 +659,7 @@ static int unmount_filesystems(zlog_t *zlogp, zoneid_t zoneid, boolean_t unmount_cmd) { int error = 0; + int fail = 0; FILE *mnttab; struct mnttab *mnts; uint_t nmnt; @@ -711,18 +747,39 @@ unmount_filesystems(zlog_t *zlogp, zoneid_t zoneid, boolean_t unmount_cmd) if (umount2(path, MS_FORCE) == 0) { unmounted = B_TRUE; stuck = B_FALSE; + fail = 0; } else { /* - * The first failure indicates a - * mount we won't be able to get - * rid of automatically, so we - * bail. + * We may hit a failure here if there + * is an app in the GZ with an open + * pipe into the zone (commonly into + * the zone's /var/run). This type + * of app will notice the closed + * connection and cleanup, but it may + * take a while and we have no easy + * way to notice that. To deal with + * this case, we will wait and retry + * a few times before we give up. */ - error++; - zerror(zlogp, B_FALSE, - "unable to unmount '%s'", path); - free_mnttable(mnts, nmnt); - goto out; + fail++; + if (fail < (UMOUNT_RETRIES - 1)) { + zerror(zlogp, B_FALSE, + "unable to unmount '%s', " + "retrying in 2 seconds", + path); + (void) sleep(2); + } else if (fail > UMOUNT_RETRIES) { + error++; + zerror(zlogp, B_FALSE, + "unmount of '%s' failed", + path); + free_mnttable(mnts, nmnt); + goto out; + } else { + /* Try the hook 2 times */ + brand_umount_cleanup(zlogp, + path); + } } } /* @@ -1060,23 +1117,10 @@ mount_one_dev_symlink_cb(void *arg, const char *source, const char *target) int vplat_get_iptype(zlog_t *zlogp, zone_iptype_t *iptypep) { - zone_dochandle_t handle; - - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (zonecfg_get_iptype(handle, iptypep) != Z_OK) { + if (zonecfg_get_iptype(snap_hndl, iptypep) != Z_OK) { zerror(zlogp, B_FALSE, "invalid ip-type configuration"); - zonecfg_fini_handle(handle); return (-1); } - zonecfg_fini_handle(handle); return (0); } @@ -1089,14 +1133,13 @@ static int mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) { char brand[MAXNAMELEN]; - zone_dochandle_t handle = NULL; brand_handle_t bh = NULL; struct zone_devtab ztab; di_prof_t prof = NULL; int err; int retval = -1; zone_iptype_t iptype; - const char *curr_iptype; + const char *curr_iptype = NULL; if (di_prof_init(devpath, &prof)) { zerror(zlogp, B_TRUE, "failed to initialize profile"); @@ -1131,6 +1174,8 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) curr_iptype = "exclusive"; break; } + if (curr_iptype == NULL) + abort(); if (brand_platform_iter_devices(bh, zone_name, mount_one_dev_device_cb, prof, curr_iptype) != 0) { @@ -1145,28 +1190,19 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) } /* Add user-specified devices and directories */ - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_FALSE, "can't initialize zone handle"); - goto cleanup; - } - if (err = zonecfg_get_handle(zone_name, handle)) { - zerror(zlogp, B_FALSE, "can't get handle for zone " - "%s: %s", zone_name, zonecfg_strerror(err)); - goto cleanup; - } - if (err = zonecfg_setdevent(handle)) { + if ((err = zonecfg_setdevent(snap_hndl)) != 0) { zerror(zlogp, B_FALSE, "%s: %s", zone_name, zonecfg_strerror(err)); goto cleanup; } - while (zonecfg_getdevent(handle, &ztab) == Z_OK) { + while (zonecfg_getdevent(snap_hndl, &ztab) == Z_OK) { if (di_prof_add_dev(prof, ztab.zone_dev_match)) { zerror(zlogp, B_TRUE, "failed to add " "user-specified device"); goto cleanup; } } - (void) zonecfg_enddevent(handle); + (void) zonecfg_enddevent(snap_hndl); /* Send profile to kernel */ if (di_prof_commit(prof)) { @@ -1179,8 +1215,6 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) cleanup: if (bh != NULL) brand_close(bh); - if (handle != NULL) - zonecfg_fini_handle(handle); if (prof) di_prof_fini(prof); return (retval); @@ -1670,12 +1704,10 @@ static int mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) { char rootpath[MAXPATHLEN]; - char zonepath[MAXPATHLEN]; char brand[MAXNAMELEN]; char luroot[MAXPATHLEN]; int i, num_fs = 0; struct zone_fstab *fs_ptr = NULL; - zone_dochandle_t handle = NULL; zone_state_t zstate; brand_handle_t bh; plat_gmount_cb_data_t cb; @@ -1689,22 +1721,12 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) goto bad; } - if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { - zerror(zlogp, B_TRUE, "unable to determine zone path"); - goto bad; - } - if (zone_get_rootpath(zone_name, rootpath, sizeof (rootpath)) != Z_OK) { zerror(zlogp, B_TRUE, "unable to determine zone root"); goto bad; } - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - goto bad; - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK || - zonecfg_setfsent(handle) != Z_OK) { + if (zonecfg_setfsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "invalid configuration"); goto bad; } @@ -1722,7 +1744,6 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) /* Get a handle to the brand info for this zone */ if ((bh = brand_open(brand)) == NULL) { zerror(zlogp, B_FALSE, "unable to determine zone brand"); - zonecfg_fini_handle(handle); return (-1); } @@ -1737,7 +1758,6 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) plat_gmount_cb, &cb) != 0) { zerror(zlogp, B_FALSE, "unable to mount filesystems"); brand_close(bh); - zonecfg_fini_handle(handle); return (-1); } brand_close(bh); @@ -1748,13 +1768,10 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) * higher level directories (e.g., /usr) get mounted before * any beneath them (e.g., /usr/local). */ - if (mount_filesystems_fsent(handle, zlogp, &fs_ptr, &num_fs, + if (mount_filesystems_fsent(snap_hndl, zlogp, &fs_ptr, &num_fs, mount_cmd) != 0) goto bad; - zonecfg_fini_handle(handle); - handle = NULL; - /* * Normally when we mount a zone all the zone filesystems * get mounted relative to rootpath, which is usually @@ -1833,8 +1850,6 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) return (0); bad: - if (handle != NULL) - zonecfg_fini_handle(handle); free_fs_data(fs_ptr, num_fs); return (-1); } @@ -2190,13 +2205,7 @@ configure_one_interface(zlog_t *zlogp, zoneid_t zone_id, if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) { /* * Here, we know that the interface can't be brought up. - * A similar warning message was already printed out to - * the console by zoneadm(1M) so instead we log the - * message to syslog and continue. */ - zerror(&logsys, B_TRUE, "WARNING: skipping network interface " - "'%s' which may not be present/plumbed in the " - "global zone.", lifr.lifr_name); (void) close(s); return (Z_OK); } @@ -2409,7 +2418,6 @@ bad: static int configure_shared_network_interfaces(zlog_t *zlogp) { - zone_dochandle_t handle; struct zone_nwiftab nwiftab, loopback_iftab; zoneid_t zoneid; @@ -2418,29 +2426,19 @@ configure_shared_network_interfaces(zlog_t *zlogp) return (-1); } - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (zonecfg_setnwifent(handle) == Z_OK) { + if (zonecfg_setnwifent(snap_hndl) == Z_OK) { for (;;) { - if (zonecfg_getnwifent(handle, &nwiftab) != Z_OK) + if (zonecfg_getnwifent(snap_hndl, &nwiftab) != Z_OK) break; + nwifent_free_attrs(&nwiftab); if (configure_one_interface(zlogp, zoneid, &nwiftab) != Z_OK) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); return (-1); } } - (void) zonecfg_endnwifent(handle); + (void) zonecfg_endnwifent(snap_hndl); } - zonecfg_fini_handle(handle); if (is_system_labeled()) { /* * Labeled zones share the loopback interface @@ -2894,7 +2892,6 @@ free_ip_interface(zone_addr_list_t *zalist) static int configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) { - zone_dochandle_t handle; struct zone_nwiftab nwiftab; char rootpath[MAXPATHLEN]; char path[MAXPATHLEN]; @@ -2903,30 +2900,18 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) boolean_t added = B_FALSE; zone_addr_list_t *zalist = NULL, *new; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - - if (zonecfg_setnwifent(handle) != Z_OK) { - zonecfg_fini_handle(handle); + if (zonecfg_setnwifent(snap_hndl) != Z_OK) return (0); - } for (;;) { - if (zonecfg_getnwifent(handle, &nwiftab) != Z_OK) + if (zonecfg_getnwifent(snap_hndl, &nwiftab) != Z_OK) break; + nwifent_free_attrs(&nwiftab); if (prof == NULL) { if (zone_get_devroot(zone_name, rootpath, sizeof (rootpath)) != Z_OK) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); zerror(zlogp, B_TRUE, "unable to determine dev root"); return (-1); @@ -2934,8 +2919,7 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) (void) snprintf(path, sizeof (path), "%s%s", rootpath, "/dev"); if (di_prof_init(path, &prof) != 0) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); zerror(zlogp, B_TRUE, "failed to initialize profile"); return (-1); @@ -2959,17 +2943,17 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) nwiftab.zone_nwif_physical) == 0) { added = B_TRUE; } else { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); - zerror(zlogp, B_TRUE, "failed to add network device"); - return (-1); + /* + * Failed to add network device, but the brand hook + * might be doing this for us, so keep silent. + */ + continue; } /* set up the new IP interface, and add them all later */ new = malloc(sizeof (*new)); if (new == NULL) { zerror(zlogp, B_TRUE, "no memory for %s", nwiftab.zone_nwif_physical); - zonecfg_fini_handle(handle); free_ip_interface(zalist); } bzero(new, sizeof (*new)); @@ -2979,16 +2963,14 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) } if (zalist != NULL) { if ((errno = add_net(zlogp, zoneid, zalist)) != 0) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); zerror(zlogp, B_TRUE, "failed to add address"); free_ip_interface(zalist); return (-1); } free_ip_interface(zalist); } - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); if (prof != NULL && added) { if (di_prof_commit(prof) != 0) { @@ -3124,48 +3106,23 @@ remove_datalink_protect(zlog_t *zlogp, zoneid_t zoneid) /* datalink does not belong to the GZ */ continue; } - if (dlstatus != DLADM_STATUS_OK) { + if (dlstatus != DLADM_STATUS_OK) zerror(zlogp, B_FALSE, + "clear 'protection' link property: %s", dladm_status2str(dlstatus, dlerr)); - free(dllinks); - return (-1); - } + dlstatus = dladm_set_linkprop(dld_handle, *dllink, "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE); - if (dlstatus != DLADM_STATUS_OK) { + if (dlstatus != DLADM_STATUS_OK) zerror(zlogp, B_FALSE, + "clear 'allowed-ips' link property: %s", dladm_status2str(dlstatus, dlerr)); - free(dllinks); - return (-1); - } } free(dllinks); return (0); } static int -unconfigure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) -{ - int dlnum = 0; - - /* - * The kernel shutdown callback for the dls module should have removed - * all datalinks from this zone. If any remain, then there's a - * problem. - */ - if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) { - zerror(zlogp, B_TRUE, "unable to list network interfaces"); - return (-1); - } - if (dlnum != 0) { - zerror(zlogp, B_FALSE, - "datalinks remain in zone after shutdown"); - return (-1); - } - return (0); -} - -static int tcp_abort_conn(zlog_t *zlogp, zoneid_t zoneid, const struct sockaddr_storage *local, const struct sockaddr_storage *remote) { @@ -3247,26 +3204,14 @@ static int get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) { int error = -1; - zone_dochandle_t handle; char *privname = NULL; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (ALT_MOUNT(mount_cmd)) { zone_iptype_t iptype; - const char *curr_iptype; + const char *curr_iptype = NULL; - if (zonecfg_get_iptype(handle, &iptype) != Z_OK) { + if (zonecfg_get_iptype(snap_hndl, &iptype) != Z_OK) { zerror(zlogp, B_TRUE, "unable to determine ip-type"); - zonecfg_fini_handle(handle); return (-1); } @@ -3279,17 +3224,15 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) break; } - if (zonecfg_default_privset(privs, curr_iptype) == Z_OK) { - zonecfg_fini_handle(handle); + if (zonecfg_default_privset(privs, curr_iptype) == Z_OK) return (0); - } + zerror(zlogp, B_FALSE, "failed to determine the zone's default privilege set"); - zonecfg_fini_handle(handle); return (-1); } - switch (zonecfg_get_privset(handle, privs, &privname)) { + switch (zonecfg_get_privset(snap_hndl, privs, &privname)) { case Z_OK: error = 0; break; @@ -3312,10 +3255,22 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) } free(privname); - zonecfg_fini_handle(handle); return (error); } +static char * +zone_proj_rctl(const char *name) +{ + int i; + + for (i = 0; zone_proj_rctl_map[i].zpr_zone_rctl != NULL; i++) { + if (strcmp(name, zone_proj_rctl_map[i].zpr_zone_rctl) == 0) { + return (zone_proj_rctl_map[i].zpr_project_rctl); + } + } + return (NULL); +} + static int get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) { @@ -3325,7 +3280,6 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) nvlist_t **nvlv = NULL; int rctlcount = 0; int error = -1; - zone_dochandle_t handle; struct zone_rctltab rctltab; rctlblk_t *rctlblk = NULL; uint64_t maxlwps; @@ -3334,16 +3288,6 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) *bufp = NULL; *bufsizep = 0; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - rctltab.zone_rctl_valptr = NULL; if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { zerror(zlogp, B_TRUE, "%s failed", "nvlist_alloc"); @@ -3356,18 +3300,18 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) * max-processes property. If only the max-processes property is set, * we add a max-lwps property with a limit derived from max-processes. */ - if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPROCS, &maxprocs) + if (zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXPROCS, &maxprocs) == Z_OK && - zonecfg_get_aliased_rctl(handle, ALIAS_MAXLWPS, &maxlwps) + zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXLWPS, &maxlwps) == Z_NO_ENTRY) { - if (zonecfg_set_aliased_rctl(handle, ALIAS_MAXLWPS, + if (zonecfg_set_aliased_rctl(snap_hndl, ALIAS_MAXLWPS, maxprocs * LWPS_PER_PROCESS) != Z_OK) { zerror(zlogp, B_FALSE, "unable to set max-lwps alias"); goto out; } } - if (zonecfg_setrctlent(handle) != Z_OK) { + if (zonecfg_setrctlent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setrctlent"); goto out; } @@ -3376,10 +3320,11 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) zerror(zlogp, B_TRUE, "memory allocation failed"); goto out; } - while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { + while (zonecfg_getrctlent(snap_hndl, &rctltab) == Z_OK) { struct zone_rctlvaltab *rctlval; uint_t i, count; const char *name = rctltab.zone_rctl_name; + char *proj_nm; /* zoneadm should have already warned about unknown rctls. */ if (!zonecfg_is_rctl(name)) { @@ -3446,6 +3391,26 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) } zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); rctltab.zone_rctl_valptr = NULL; + + /* + * With no action on our part we will start zsched with the + * project rctl values for our (zoneadmd) current project. For + * brands running a variant of Illumos, that's not a problem + * since they will setup their own projects, but for a + * non-native brand like lx, where there are no projects, we + * want to start things up with the same project rctls as the + * corresponding zone rctls, since nothing within the zone will + * ever change the project rctls. + */ + if ((proj_nm = zone_proj_rctl(name)) != NULL) { + if (nvlist_add_nvlist_array(nvl, proj_nm, nvlv, count) + != 0) { + zerror(zlogp, B_FALSE, + "nvlist_add_nvlist_arrays failed"); + goto out; + } + } + if (nvlist_add_nvlist_array(nvl, (char *)name, nvlv, count) != 0) { zerror(zlogp, B_FALSE, "%s failed", @@ -3458,7 +3423,7 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) nvlv = NULL; rctlcount++; } - (void) zonecfg_endrctlent(handle); + (void) zonecfg_endrctlent(snap_hndl); if (rctlcount == 0) { error = 0; @@ -3483,8 +3448,6 @@ out: nvlist_free(nvl); if (nvlv != NULL) free(nvlv); - if (handle != NULL) - zonecfg_fini_handle(handle); return (error); } @@ -3500,7 +3463,7 @@ get_implicit_datasets(zlog_t *zlogp, char **retstr) > sizeof (cmdbuf)) return (-1); - if (do_subproc(zlogp, cmdbuf, retstr) != 0) + if (do_subproc(zlogp, cmdbuf, retstr, B_FALSE) != 0) return (-1); return (0); @@ -3509,7 +3472,6 @@ get_implicit_datasets(zlog_t *zlogp, char **retstr) static int get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) { - zone_dochandle_t handle; struct zone_dstab dstab; size_t total, offset, len; int error = -1; @@ -3520,30 +3482,20 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) *bufp = NULL; *bufsizep = 0; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (get_implicit_datasets(zlogp, &implicit_datasets) != 0) { zerror(zlogp, B_FALSE, "getting implicit datasets failed"); goto out; } - if (zonecfg_setdsent(handle) != Z_OK) { + if (zonecfg_setdsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setdsent"); goto out; } total = 0; - while (zonecfg_getdsent(handle, &dstab) == Z_OK) + while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) total += strlen(dstab.zone_dataset_name) + 1; - (void) zonecfg_enddsent(handle); + (void) zonecfg_enddsent(snap_hndl); if (implicit_datasets != NULL) implicit_len = strlen(implicit_datasets); @@ -3560,12 +3512,12 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) goto out; } - if (zonecfg_setdsent(handle) != Z_OK) { + if (zonecfg_setdsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setdsent"); goto out; } offset = 0; - while (zonecfg_getdsent(handle, &dstab) == Z_OK) { + while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) { len = strlen(dstab.zone_dataset_name); (void) strlcpy(str + offset, dstab.zone_dataset_name, total - offset); @@ -3573,7 +3525,7 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) if (offset < total - 1) str[offset++] = ','; } - (void) zonecfg_enddsent(handle); + (void) zonecfg_enddsent(snap_hndl); if (implicit_len > 0) (void) strlcpy(str + offset, implicit_datasets, total - offset); @@ -3585,8 +3537,6 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) out: if (error != 0 && str != NULL) free(str); - if (handle != NULL) - zonecfg_fini_handle(handle); if (implicit_datasets != NULL) free(implicit_datasets); @@ -3596,40 +3546,26 @@ out: static int validate_datasets(zlog_t *zlogp) { - zone_dochandle_t handle; struct zone_dstab dstab; zfs_handle_t *zhp; libzfs_handle_t *hdl; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { + if (zonecfg_setdsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - - if (zonecfg_setdsent(handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); return (-1); } if ((hdl = libzfs_init()) == NULL) { zerror(zlogp, B_FALSE, "opening ZFS library"); - zonecfg_fini_handle(handle); return (-1); } - while (zonecfg_getdsent(handle, &dstab) == Z_OK) { + while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) { if ((zhp = zfs_open(hdl, dstab.zone_dataset_name, ZFS_TYPE_FILESYSTEM)) == NULL) { zerror(zlogp, B_FALSE, "cannot open ZFS dataset '%s'", dstab.zone_dataset_name); - zonecfg_fini_handle(handle); libzfs_fini(hdl); return (-1); } @@ -3644,7 +3580,6 @@ validate_datasets(zlog_t *zlogp) zerror(zlogp, B_FALSE, "cannot set 'zoned' " "property for ZFS dataset '%s'\n", dstab.zone_dataset_name); - zonecfg_fini_handle(handle); zfs_close(zhp); libzfs_fini(hdl); return (-1); @@ -3652,9 +3587,8 @@ validate_datasets(zlog_t *zlogp) zfs_close(zhp); } - (void) zonecfg_enddsent(handle); + (void) zonecfg_enddsent(snap_hndl); - zonecfg_fini_handle(handle); libzfs_fini(hdl); return (0); @@ -3708,17 +3642,11 @@ validate_rootds_label(zlog_t *zlogp, char *rootpath, m_label_t *zone_sl) zfs_handle_t *zhp; libzfs_handle_t *hdl; m_label_t ds_sl; - char zonepath[MAXPATHLEN]; char ds_hexsl[MAXNAMELEN]; if (!is_system_labeled()) return (0); - if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { - zerror(zlogp, B_TRUE, "unable to determine zone path"); - return (-1); - } - if (!is_zonepath_zfs(zonepath)) return (0); @@ -4388,62 +4316,25 @@ duplicate_reachable_path(zlog_t *zlogp, const char *rootpath) } /* - * Set memory cap and pool info for the zone's resource management - * configuration. + * Set pool info for the zone's resource management configuration. */ static int setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) { int res; uint64_t tmp; - struct zone_mcaptab mcap; char sched[MAXNAMELEN]; - zone_dochandle_t handle = NULL; char pool_err[128]; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (Z_BAD_HANDLE); - } - - if ((res = zonecfg_get_snapshot_handle(zone_name, handle)) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (res); - } - - /* - * If a memory cap is configured, set the cap in the kernel using - * zone_setattr() and make sure the rcapd SMF service is enabled. - */ - if (zonecfg_getmcapent(handle, &mcap) == Z_OK) { - uint64_t num; - char smf_err[128]; - - num = (uint64_t)strtoull(mcap.zone_physmem_cap, NULL, 10); - if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { - zerror(zlogp, B_TRUE, "could not set zone memory cap"); - zonecfg_fini_handle(handle); - return (Z_INVAL); - } - - if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) { - zerror(zlogp, B_FALSE, "enabling system/rcap service " - "failed: %s", smf_err); - zonecfg_fini_handle(handle); - return (Z_INVAL); - } - } - /* Get the scheduling class set in the zone configuration. */ - if (zonecfg_get_sched_class(handle, sched, sizeof (sched)) == Z_OK && + if (zonecfg_get_sched_class(snap_hndl, sched, sizeof (sched)) == Z_OK && strlen(sched) > 0) { if (zone_setattr(zoneid, ZONE_ATTR_SCHED_CLASS, sched, strlen(sched)) == -1) zerror(zlogp, B_TRUE, "WARNING: unable to set the " "default scheduling class"); - } else if (zonecfg_get_aliased_rctl(handle, ALIAS_SHARES, &tmp) + } else if (zonecfg_get_aliased_rctl(snap_hndl, ALIAS_SHARES, &tmp) == Z_OK) { /* * If the zone has the zone.cpu-shares rctl set then we want to @@ -4454,7 +4345,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) */ char class_name[PC_CLNMSZ]; - if (zonecfg_get_dflt_sched_class(handle, class_name, + if (zonecfg_get_dflt_sched_class(snap_hndl, class_name, sizeof (class_name)) != Z_OK) { zerror(zlogp, B_FALSE, "WARNING: unable to determine " "the zone's scheduling class"); @@ -4487,7 +4378,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) * right thing in all cases (reuse or create) based on the current * zonecfg. */ - if ((res = zonecfg_bind_tmp_pool(handle, zoneid, pool_err, + if ((res = zonecfg_bind_tmp_pool(snap_hndl, zoneid, pool_err, sizeof (pool_err))) != Z_OK) { if (res == Z_POOL || res == Z_POOL_CREATE || res == Z_POOL_BIND) zerror(zlogp, B_FALSE, "%s: %s\ndedicated-cpu setting " @@ -4496,14 +4387,13 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) else zerror(zlogp, B_FALSE, "could not bind zone to " "temporary pool: %s", zonecfg_strerror(res)); - zonecfg_fini_handle(handle); return (Z_POOL_BIND); } /* * Check if we need to warn about poold not being enabled. */ - if (zonecfg_warn_poold(handle)) { + if (zonecfg_warn_poold(snap_hndl)) { zerror(zlogp, B_FALSE, "WARNING: A range of dedicated-cpus has " "been specified\nbut the dynamic pool service is not " "enabled.\nThe system will not dynamically adjust the\n" @@ -4513,7 +4403,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) } /* The following is a warning, not an error. */ - if ((res = zonecfg_bind_pool(handle, zoneid, pool_err, + if ((res = zonecfg_bind_pool(snap_hndl, zoneid, pool_err, sizeof (pool_err))) != Z_OK) { if (res == Z_POOL_BIND) zerror(zlogp, B_FALSE, "WARNING: unable to bind to " @@ -4527,10 +4417,9 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) } /* Update saved pool name in case it has changed */ - (void) zonecfg_get_poolname(handle, zone_name, pool_name, + (void) zonecfg_get_poolname(snap_hndl, zone_name, pool_name, sizeof (pool_name)); - zonecfg_fini_handle(handle); return (Z_OK); } @@ -4631,33 +4520,28 @@ setup_zone_fs_allowed(zone_dochandle_t handle, zlog_t *zlogp, zoneid_t zoneid) } static int -setup_zone_attrs(zlog_t *zlogp, char *zone_namep, zoneid_t zoneid) +setup_zone_attrs(zlog_t *zlogp, zoneid_t zoneid) { - zone_dochandle_t handle; int res = Z_OK; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (Z_BAD_HANDLE); - } - if ((res = zonecfg_get_snapshot_handle(zone_namep, handle)) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - goto out; - } - - if ((res = setup_zone_hostid(handle, zlogp, zoneid)) != Z_OK) + if ((res = setup_zone_hostid(snap_hndl, zlogp, zoneid)) != Z_OK) goto out; - if ((res = setup_zone_fs_allowed(handle, zlogp, zoneid)) != Z_OK) + if ((res = setup_zone_fs_allowed(snap_hndl, zlogp, zoneid)) != Z_OK) goto out; out: - zonecfg_fini_handle(handle); return (res); } +/* + * The zone_did is a persistent debug ID. Each zone should have a unique ID + * in the kernel. This is used for things like DTrace which want to monitor + * zones across reboots. They can't use the zoneid since that changes on + * each boot. + */ zoneid_t -vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) +vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zone_did) { zoneid_t rval = -1; priv_set_t *privs; @@ -4673,7 +4557,7 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) tsol_zcent_t *zcent = NULL; int match = 0; int doi = 0; - int flags; + int flags = -1; zone_iptype_t iptype; if (zone_get_rootpath(zone_name, rootpath, sizeof (rootpath)) != Z_OK) { @@ -4695,6 +4579,8 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) flags = ZCF_NET_EXCL; break; } + if (flags == -1) + abort(); if ((privs = priv_allocset()) == NULL) { zerror(zlogp, B_TRUE, "%s failed", "priv_allocset"); @@ -4798,7 +4684,7 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) xerr = 0; if ((zoneid = zone_create(kzone, rootpath, privs, rctlbuf, rctlbufsz, zfsbuf, zfsbufsz, &xerr, match, doi, zlabel, - flags)) == -1) { + flags, zone_did)) == -1) { if (xerr == ZE_AREMOUNTS) { if (zonecfg_find_mounts(rootpath, NULL, NULL) < 1) { zerror(zlogp, B_FALSE, @@ -4844,7 +4730,7 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) struct brand_attr attr; char modname[MAXPATHLEN]; - if (setup_zone_attrs(zlogp, zone_name, zoneid) != Z_OK) + if (setup_zone_attrs(zlogp, zoneid) != Z_OK) goto error; if ((bh = brand_open(brand_name)) == NULL) { @@ -4902,6 +4788,8 @@ error: } if (rctlbuf != NULL) free(rctlbuf); + if (zfsbuf != NULL) + free(zfsbuf); priv_freeset(privs); if (fp != NULL) zonecfg_close_scratch(fp); @@ -4990,7 +4878,7 @@ write_index_file(zoneid_t zoneid) int vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) { - char zonepath[MAXPATHLEN]; + char zpath[MAXPATHLEN]; if (mount_cmd == Z_MNT_BOOT && validate_datasets(zlogp) != 0) { lofs_discard_mnttab(); @@ -5001,15 +4889,11 @@ vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) * Before we try to mount filesystems we need to create the * attribute backing store for /dev */ - if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { - lofs_discard_mnttab(); - return (-1); - } - resolve_lofs(zlogp, zonepath, sizeof (zonepath)); + (void) strlcpy(zpath, zonepath, sizeof (zpath)); + resolve_lofs(zlogp, zpath, sizeof (zpath)); /* Make /dev directory owned by root, grouped sys */ - if (make_one_dir(zlogp, zonepath, "/dev", DEFAULT_DIR_MODE, - 0, 3) != 0) { + if (make_one_dir(zlogp, zpath, "/dev", DEFAULT_DIR_MODE, 0, 3) != 0) { lofs_discard_mnttab(); return (-1); } @@ -5044,6 +4928,8 @@ vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) return (-1); } break; + default: + abort(); } } @@ -5119,13 +5005,13 @@ unmounted: } int -vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) +vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, + boolean_t debug) { char *kzone; zoneid_t zoneid; int res; char pool_err[128]; - char zpath[MAXPATHLEN]; char cmdbuf[MAXPATHLEN]; brand_handle_t bh = NULL; dladm_status_t status; @@ -5158,16 +5044,12 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) goto error; } - if (remove_datalink_pool(zlogp, zoneid) != 0) { + if (remove_datalink_pool(zlogp, zoneid) != 0) zerror(zlogp, B_FALSE, "unable clear datalink pool property"); - goto error; - } - if (remove_datalink_protect(zlogp, zoneid) != 0) { + if (remove_datalink_protect(zlogp, zoneid) != 0) zerror(zlogp, B_FALSE, "unable clear datalink protect property"); - goto error; - } /* * The datalinks assigned to the zone will be removed from the NGZ as @@ -5181,12 +5063,6 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) goto error; } - /* Get the zonepath of this zone */ - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - goto error; - } - /* Get a handle to the brand info for this zone */ if ((bh = brand_open(brand_name)) == NULL) { zerror(zlogp, B_FALSE, "unable to determine zone brand"); @@ -5197,7 +5073,7 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) * brand a chance to cleanup any custom configuration. */ (void) strcpy(cmdbuf, EXEC_PREFIX); - if (brand_get_halt(bh, zone_name, zpath, cmdbuf + EXEC_LEN, + if (brand_get_halt(bh, zone_name, zonepath, cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN) < 0) { brand_close(bh); zerror(zlogp, B_FALSE, "unable to determine branded zone's " @@ -5207,7 +5083,7 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) brand_close(bh); if ((strlen(cmdbuf) > EXEC_LEN) && - (do_subproc(zlogp, cmdbuf, NULL) != Z_OK)) { + (do_subproc(zlogp, cmdbuf, NULL, debug) != Z_OK)) { zerror(zlogp, B_FALSE, "%s failed", cmdbuf); goto error; } @@ -5239,12 +5115,6 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) } break; case ZS_EXCLUSIVE: - if (unconfigure_exclusive_network_interfaces(zlogp, - zoneid) != 0) { - zerror(zlogp, B_FALSE, "unable to unconfigure " - "network interfaces in zone"); - goto error; - } status = dladm_zone_halt(dld_handle, zoneid); if (status != DLADM_STATUS_OK) { zerror(zlogp, B_FALSE, "unable to notify " @@ -5281,14 +5151,9 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) if (rebooting) { struct zone_psettab pset_tab; - zone_dochandle_t handle; - if ((handle = zonecfg_init_handle()) != NULL && - zonecfg_get_handle(zone_name, handle) == Z_OK && - zonecfg_lookup_pset(handle, &pset_tab) == Z_OK) + if (zonecfg_lookup_pset(snap_hndl, &pset_tab) == Z_OK) destroy_tmp_pool = B_FALSE; - - zonecfg_fini_handle(handle); } if (destroy_tmp_pool) { diff --git a/usr/src/cmd/zoneadmd/zcons.c b/usr/src/cmd/zoneadmd/zcons.c index 1466217587..30e4a35a35 100644 --- a/usr/src/cmd/zoneadmd/zcons.c +++ b/usr/src/cmd/zoneadmd/zcons.c @@ -22,7 +22,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2012 Joyent, Inc. All rights reserved. + * Copyright 2015 Joyent, Inc. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ @@ -118,9 +118,10 @@ #define CONSOLE_SOCKPATH ZONES_TMPDIR "/%s.console_sock" +#define ZCONS_RETRY 10 + static int serverfd = -1; /* console server unix domain socket fd */ char boot_args[BOOTARGS_MAX]; -char bad_boot_arg[BOOTARGS_MAX]; /* * The eventstream is a simple one-directional flow of messages from the @@ -130,7 +131,10 @@ char bad_boot_arg[BOOTARGS_MAX]; */ static int eventstream[2]; - +/* flag used to cope with race creating master zcons devlink */ +static boolean_t master_zcons_failed = B_FALSE; +/* flag to track if we've seen a state change when there is no master zcons */ +static boolean_t state_changed = B_FALSE; int eventstream_init() @@ -322,7 +326,7 @@ destroy_console_devs(zlog_t *zlogp) * interfaces to instantiate a new zone console node. We do a lot of * sanity checking, and are careful to reuse a console if one exists. * - * Once the device is in the device tree, we kick devfsadm via di_init_devs() + * Once the device is in the device tree, we kick devfsadm via di_devlink_init() * to ensure that the appropriate symlinks (to the master and slave console * devices) are placed in /dev in the global zone. */ @@ -408,43 +412,63 @@ devlinks: * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl, * which will cause the master to retain a reference to the slave. * This prevents ttymon from blowing through the slave's STREAMS anchor. + * + * In very rare cases the open returns ENOENT if devfs doesn't have + * everything setup yet due to heavy zone startup load. Wait for + * 1 sec. and retry a few times. Even if we can't setup the zone's + * console, we still go ahead and boot the zone. */ (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); - if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + masterfd = open(conspath, O_RDWR | O_NOCTTY); + if (masterfd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (masterfd == -1) { zerror(zlogp, B_TRUE, "ERROR: could not open master side of " "zone console for %s to acquire slave handle", zone_name); - goto error; + master_zcons_failed = B_TRUE; } + (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_SLAVE_NAME); - if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + slavefd = open(conspath, O_RDWR | O_NOCTTY); + if (slavefd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (slavefd == -1) zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone" " console for %s to acquire slave handle", zone_name); - (void) close(masterfd); - goto error; - } + /* * This ioctl can occasionally return ENXIO if devfs doesn't have * everything plumbed up yet due to heavy zone startup load. Wait for * 1 sec. and retry a few times before we fail to boot the zone. */ - for (i = 0; i < 5; i++) { - if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd) - == 0) { - rv = 0; - break; - } else if (errno != ENXIO) { - break; + if (masterfd != -1 && slavefd != -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + if (ioctl(masterfd, ZC_HOLDSLAVE, + (caddr_t)(intptr_t)slavefd) == 0) { + rv = 0; + break; + } else if (errno != ENXIO) { + break; + } + (void) sleep(1); } - (void) sleep(1); + if (rv != 0) + zerror(zlogp, B_TRUE, "ERROR: error while acquiring " + "slave handle of zone console for %s", zone_name); } - if (rv != 0) - zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave " - "handle of zone console for %s", zone_name); - (void) close(slavefd); - (void) close(masterfd); + if (slavefd != -1) + (void) close(slavefd); + if (masterfd != -1) + (void) close(masterfd); error: if (ddef_hdl) @@ -517,6 +541,7 @@ get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, size_t buflen = sizeof (buf); char c = '\0'; int i = 0, r; + ucred_t *cred = NULL; /* "eat up the ident string" case, for simplicity */ if (pid == NULL) { @@ -550,18 +575,22 @@ get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, break; } + if (getpeerucred(clifd, &cred) == 0) { + *pid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } else { + return (-1); + } + /* * Parse buffer for message of the form: - * IDENT <pid> <locale> <disconnect flag> + * IDENT <locale> <disconnect flag> */ bufp = buf; if (strncmp(bufp, "IDENT ", 6) != 0) return (-1); bufp += 6; errno = 0; - *pid = strtoll(bufp, &bufp, 10); - if (errno != 0) - return (-1); while (*bufp != '\0' && isspace(*bufp)) bufp++; @@ -667,14 +696,6 @@ event_message(int clifd, char *clilocale, zone_evt_t evt, int dflag) else str = "NOTICE: Zone boot failed"; break; - case Z_EVT_ZONE_BADARGS: - /*LINTED*/ - (void) snprintf(lmsg, sizeof (lmsg), - localize_msg(clilocale, - "WARNING: Ignoring invalid boot arguments: %s"), - bad_boot_arg); - lstr = lmsg; - break; default: return; } @@ -878,7 +899,6 @@ init_console(zlog_t *zlogp) if (init_console_dev(zlogp) == -1) { zerror(zlogp, B_FALSE, "console setup: device initialization failed"); - return (-1); } if ((serverfd = init_console_sock(zlogp)) == -1) { @@ -890,6 +910,17 @@ init_console(zlog_t *zlogp) } /* + * Maintain a simple flag that tracks if we have seen at least one state + * change. This is currently only used to handle the special case where we are + * running without a console device, which is what normally drives shutdown. + */ +void +zcons_statechanged() +{ + state_changed = B_TRUE; +} + +/* * serve_console() is the master loop for driving console I/O. It is also the * routine which is ultimately responsible for "pulling the plug" on zoneadmd * when it realizes that the daemon should shut down. @@ -907,6 +938,7 @@ serve_console(zlog_t *zlogp) int masterfd; zone_state_t zstate; char conspath[MAXPATHLEN]; + static boolean_t cons_warned = B_FALSE; (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); @@ -914,6 +946,46 @@ serve_console(zlog_t *zlogp) for (;;) { masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY); if (masterfd == -1) { + if (master_zcons_failed) { + /* + * If we don't have a console and the zone is + * not shutting down, there may have been a + * race/failure with devfs while creating the + * console. In this case we want to leave the + * zone up, even without a console, so + * periodically recheck. + */ + int i; + + /* + * In the normal flow of this loop, we use + * do_console_io to give things a chance to get + * going first. However, in this case we can't + * use that, so we have to wait for at least + * one state change before checking the state. + */ + for (i = 0; i < 60; i++) { + if (state_changed) + break; + (void) sleep(1); + } + + if (i < 60 && zone_get_state(zone_name, + &zstate) == Z_OK && + (zstate == ZONE_STATE_READY || + zstate == ZONE_STATE_RUNNING)) { + if (!cons_warned) { + zerror(zlogp, B_FALSE, + "WARNING: missing zone " + "console for %s", + zone_name); + cons_warned = B_TRUE; + } + (void) sleep(ZCONS_RETRY); + continue; + } + } + zerror(zlogp, B_TRUE, "failed to open console master"); (void) mutex_lock(&lock); goto death; diff --git a/usr/src/cmd/zoneadmd/zfd.c b/usr/src/cmd/zoneadmd/zfd.c new file mode 100644 index 0000000000..45acebfdfd --- /dev/null +++ b/usr/src/cmd/zoneadmd/zfd.c @@ -0,0 +1,1344 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +/* + * Zone file descriptor support is used as a mechanism for a process inside the + * zone to either log messages to the GZ zoneadmd or as a way to interact + * directly with the process (via zlogin -I). The zfd thread is modeled on + * the zcons thread so see the comment header in zcons.c for a general overview. + * Unlike with zcons, which has a single endpoint within the zone and a single + * endpoint used by zoneadmd, we setup multiple endpoints within the zone. + * In the interactive mode we setup fd 0, 1 and 2 for use as stdin, stdout and + * stderr. In the logging mode we only setup fd 1 and 2 for use as stdout and + * stderr. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/termios.h> +#include <sys/zfd.h> +#include <sys/mkdev.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <stropts.h> +#include <thread.h> +#include <ucred.h> +#include <unistd.h> +#include <zone.h> +#include <signal.h> +#include <wchar.h> + +#include <libdevinfo.h> +#include <libdevice.h> +#include <libzonecfg.h> + +#include <syslog.h> +#include <sys/modctl.h> + +#include "zoneadmd.h" + +static zlog_t *zlogp; +static int shutting_down = 0; +static thread_t logger_tid; +static int logfd = -1; + +/* + * The eventstream is a simple one-directional flow of messages implemented + * with a pipe. It is used to wake up the poller when it needs to shutdown. + */ +static int eventstream[2] = {-1, -1}; + +#define LOGNAME "stdio.log" +#define ZLOG_MODE "zlog-mode" +#define ZFDNEX_DEVTREEPATH "/pseudo/zfdnex@2" +#define ZFDNEX_FILEPATH "/devices/pseudo/zfdnex@2" +#define SERVER_SOCKPATH ZONES_TMPDIR "/%s.server_%s" +#define ZTTY_RETRY 5 + +typedef enum { + ZLOG_NONE = 0, + ZLOG_LOG, + ZLOG_INTERACTIVE, +} zlog_mode_t; + +/* + * count_zfd_devs() and its helper count_cb() do a walk of the subtree of the + * device tree where zfd nodes are represented. The goal is to count zfd + * instances already setup for a zone with the given name. + * + * Note: this algorithm is a linear search of nodes in the zfdnex subtree + * of the device tree, and could be a scalability problem, but I don't see + * how to avoid it. + */ + +/* + * cb_data is shared by count_cb and destroy_cb for simplicity. + */ +struct cb_data { + zlog_t *zlogp; + int found; + int killed; +}; + +static int +count_cb(di_node_t node, void *arg) +{ + struct cb_data *cb = (struct cb_data *)arg; + char *prop_data; + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname", + &prop_data) != -1) { + assert(prop_data != NULL); + if (strcmp(prop_data, zone_name) == 0) { + cb->found++; + return (DI_WALK_CONTINUE); + } + } + return (DI_WALK_CONTINUE); +} + +static int +count_zfd_devs(zlog_t *zlogp) +{ + di_node_t root; + struct cb_data cb; + + bzero(&cb, sizeof (cb)); + cb.zlogp = zlogp; + + if ((root = di_init(ZFDNEX_DEVTREEPATH, DINFOCPYALL)) == DI_NODE_NIL) { + zerror(zlogp, B_TRUE, "di_init failed"); + return (-1); + } + + (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, count_cb); + di_fini(root); + return (cb.found); +} + +/* + * destroy_zfd_devs() and its helper destroy_cb() tears down any zfd instances + * associated with this zone. If things went very wrong, we might have an + * incorrect number of instances hanging around. This routine hunts down and + * tries to remove all of them. Of course, if the fd is open, the instance will + * not detach, which is a potential issue. + */ +static int +destroy_cb(di_node_t node, void *arg) +{ + struct cb_data *cb = (struct cb_data *)arg; + char *prop_data; + char *tmp; + char devpath[MAXPATHLEN]; + devctl_hdl_t hdl; + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname", + &prop_data) == -1) + return (DI_WALK_CONTINUE); + + assert(prop_data != NULL); + if (strcmp(prop_data, zone_name) != 0) { + /* this is a zfd for a different zone */ + return (DI_WALK_CONTINUE); + } + + cb->found++; + tmp = di_devfs_path(node); + (void) snprintf(devpath, sizeof (devpath), "/devices/%s", tmp); + di_devfs_path_free(tmp); + + if ((hdl = devctl_device_acquire(devpath, 0)) == NULL) { + zerror(cb->zlogp, B_TRUE, "WARNING: zfd %s found, " + "but it could not be controlled.", devpath); + return (DI_WALK_CONTINUE); + } + if (devctl_device_remove(hdl) == 0) { + cb->killed++; + } else { + zerror(cb->zlogp, B_TRUE, "WARNING: zfd %s found, " + "but it could not be removed.", devpath); + } + devctl_release(hdl); + return (DI_WALK_CONTINUE); +} + +static int +destroy_zfd_devs(zlog_t *zlogp) +{ + di_node_t root; + struct cb_data cb; + + bzero(&cb, sizeof (cb)); + cb.zlogp = zlogp; + + if ((root = di_init(ZFDNEX_DEVTREEPATH, DINFOCPYALL)) == DI_NODE_NIL) { + zerror(zlogp, B_TRUE, "di_init failed"); + return (-1); + } + + (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, destroy_cb); + + di_fini(root); + return (0); +} + +static void +make_tty(zlog_t *zlogp, int id) +{ + int i; + int fd = -1; + char stdpath[MAXPATHLEN]; + + /* + * Open the master side of the dev and issue the ZFD_MAKETTY ioctl, + * which will cause the the various tty-related streams modules to be + * pushed when the slave opens the device. + * + * In very rare cases the open returns ENOENT if devfs doesn't have + * everything setup yet due to heavy zone startup load. Wait for + * 1 sec. and retry a few times. Even if we can't setup tty mode + * we still move on. + */ + (void) snprintf(stdpath, sizeof (stdpath), "/dev/zfd/%s/master/%d", + zone_name, id); + + for (i = 0; !shutting_down && i < ZTTY_RETRY; i++) { + fd = open(stdpath, O_RDWR | O_NOCTTY); + if (fd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (fd == -1) { + zerror(zlogp, B_TRUE, "ERROR: could not open zfd %d for " + "zone %s to set tty mode", id, zone_name); + } else { + /* + * This ioctl can occasionally return ENXIO if devfs doesn't + * have everything plumbed up yet due to heavy zone startup + * load. Wait for 1 sec. and retry a few times before we give + * up. + */ + for (i = 0; !shutting_down && i < ZTTY_RETRY; i++) { + if (ioctl(fd, ZFD_MAKETTY) == 0) { + break; + } else if (errno != ENXIO) { + break; + } + (void) sleep(1); + } + } + + if (fd != -1) + (void) close(fd); +} + +/* + * init_zfd_devs() drives the device-tree configuration of the zone fd devices. + * The general strategy is to use the libdevice (devctl) interfaces to + * instantiate 3 new zone fd nodes. We do a lot of sanity checking, and + * are careful to reuse a dev if one exists. + * + * Once the devices are in the device tree, we kick devfsadm via + * di_devlink_init() to ensure that the appropriate symlinks (to the master and + * slave fd devices) are placed in /dev in the global zone. + */ +static int +init_zfd_dev(zlog_t *zlogp, devctl_hdl_t bus_hdl, int id) +{ + int rv = -1; + devctl_ddef_t ddef_hdl = NULL; + devctl_hdl_t dev_hdl = NULL; + + if ((ddef_hdl = devctl_ddef_alloc("zfd", 0)) == NULL) { + zerror(zlogp, B_TRUE, "failed to allocate ddef handle"); + goto error; + } + + /* + * Set four properties on this node; the first is the name of the + * zone; the second is a flag which lets pseudo know that it is + * OK to automatically allocate an instance # for this device; + * the third tells the device framework not to auto-detach this + * node-- we need the node to still be there when we ask devfsadmd + * to make links, and when we need to open it. + */ + if (devctl_ddef_string(ddef_hdl, "zfd_zname", zone_name) == -1) { + zerror(zlogp, B_TRUE, "failed to create zfd_zname property"); + goto error; + } + if (devctl_ddef_int(ddef_hdl, "zfd_id", id) == -1) { + zerror(zlogp, B_TRUE, "failed to create zfd_id property"); + goto error; + } + if (devctl_ddef_int(ddef_hdl, "auto-assign-instance", 1) == -1) { + zerror(zlogp, B_TRUE, "failed to create auto-assign-instance " + "property"); + goto error; + } + if (devctl_ddef_int(ddef_hdl, "ddi-no-autodetach", 1) == -1) { + zerror(zlogp, B_TRUE, "failed to create ddi-no-auto-detach " + "property"); + goto error; + } + if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl) == -1) { + zerror(zlogp, B_TRUE, "failed to create zfd node"); + goto error; + } + rv = 0; + +error: + if (ddef_hdl) + devctl_ddef_free(ddef_hdl); + if (dev_hdl) + devctl_release(dev_hdl); + return (rv); +} + +static int +init_zfd_devs(zlog_t *zlogp, zlog_mode_t mode) +{ + devctl_hdl_t bus_hdl = NULL; + di_devlink_handle_t dl = NULL; + int rv = -1; + int ndevs; + int reqdevs; + int i; + + /* + * Three zfd devices are required for log mode. + * Interactive mode needs only one. + */ + reqdevs = (mode != ZLOG_INTERACTIVE) ? 3 : 1; + + /* + * Don't re-setup zone fd devs if they already exist; just + * skip ahead to making devlinks, which we do for sanity's sake. + */ + ndevs = count_zfd_devs(zlogp); + + if (ndevs == reqdevs) + goto devlinks; + + if (ndevs > 0 || ndevs == -1) { + if (destroy_zfd_devs(zlogp) == -1) + goto error; + } + + /* + * Time to make the devices. + */ + if ((bus_hdl = devctl_bus_acquire(ZFDNEX_FILEPATH, 0)) == NULL) { + zerror(zlogp, B_TRUE, "devctl_bus_acquire failed"); + goto error; + } + + for (i = 0; i < reqdevs; i++) { + if (init_zfd_dev(zlogp, bus_hdl, i) != 0) + goto error; + } + +devlinks: + if ((dl = di_devlink_init("zfd", DI_MAKE_LINK)) == NULL) { + zerror(zlogp, B_TRUE, "failed to create devlinks"); + goto error; + } + + (void) di_devlink_fini(&dl); + rv = 0; + + if (mode == ZLOG_INTERACTIVE) { + /* We want to look like a tty. */ + make_tty(zlogp, 0); + } + +error: + if (bus_hdl) + devctl_release(bus_hdl); + return (rv); +} + +static int +init_server_sock(zlog_t *zlogp, int *servfd, char *nm) +{ + int resfd = -1; + struct sockaddr_un servaddr; + + bzero(&servaddr, sizeof (servaddr)); + servaddr.sun_family = AF_UNIX; + (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), + SERVER_SOCKPATH, zone_name, nm); + + if ((resfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + zerror(zlogp, B_TRUE, "server setup: could not create socket"); + goto err; + } + (void) unlink(servaddr.sun_path); + + if (bind(resfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) + == -1) { + zerror(zlogp, B_TRUE, + "server setup: could not bind to socket"); + goto err; + } + + if (listen(resfd, 4) == -1) { + zerror(zlogp, B_TRUE, + "server setup: could not listen on socket"); + goto err; + } + + *servfd = resfd; + return (0); + +err: + (void) unlink(servaddr.sun_path); + if (resfd != -1) + (void) close(resfd); + return (-1); +} + +static void +destroy_server_sock(int servfd, char *nm) +{ + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), SERVER_SOCKPATH, zone_name, nm); + (void) unlink(path); + (void) shutdown(servfd, SHUT_RDWR); + (void) close(servfd); +} + +/* + * Read the "ident" string from the client's descriptor; this routine also + * tolerates being called with pid=NULL, for times when you want to "eat" + * the ident string from a client without saving it. + */ +static int +get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, + uint_t *flagsp) +{ + char buf[BUFSIZ], *bufp; + size_t buflen = sizeof (buf); + char c = '\0'; + int i = 0, r; + ucred_t *cred = NULL; + + /* "eat up the ident string" case, for simplicity */ + if (pid == NULL) { + assert(locale == NULL && locale_len == 0); + while (read(clifd, &c, 1) == 1) { + if (c == '\n') + return (0); + } + } + + bzero(buf, sizeof (buf)); + while ((buflen > 1) && (r = read(clifd, &c, 1)) == 1) { + buflen--; + if (c == '\n') + break; + + buf[i] = c; + i++; + } + if (r == -1) + return (-1); + + /* + * We've filled the buffer, but still haven't seen \n. Keep eating + * until we find it; we don't expect this to happen, but this is + * defensive. + */ + if (c != '\n') { + while ((r = read(clifd, &c, sizeof (c))) > 0) + if (c == '\n') + break; + } + + /* + * Parse buffer for message of the form: + * IDENT <locale> <flags> + */ + bufp = buf; + if (strncmp(bufp, "IDENT ", 6) != 0) + return (-1); + bufp += 6; + + if (getpeerucred(clifd, &cred) == 0) { + *pid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } else { + return (-1); + } + + while (*bufp != '\0' && isspace(*bufp)) + bufp++; + buflen = strlen(bufp) - 1; + bufp[buflen - 1] = '\0'; + (void) strlcpy(locale, bufp, locale_len); + + *flagsp = atoi(&bufp[buflen]); + + return (0); +} + +static int +accept_client(int servfd, pid_t *pid, char *locale, size_t locale_len, + uint_t *flagsp) +{ + int connfd; + struct sockaddr_un cliaddr; + socklen_t clilen; + int flags; + + clilen = sizeof (cliaddr); + connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); + if (connfd == -1) + return (-1); + if (pid != NULL) { + if (get_client_ident(connfd, pid, locale, locale_len, flagsp) + == -1) { + (void) shutdown(connfd, SHUT_RDWR); + (void) close(connfd); + return (-1); + } + (void) write(connfd, "OK\n", 3); + } + + flags = fcntl(connfd, F_GETFL, 0); + if (flags != -1) + (void) fcntl(connfd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC); + + return (connfd); +} + +static void +reject_client(int servfd, pid_t clientpid) +{ + int connfd; + struct sockaddr_un cliaddr; + socklen_t clilen; + char nak[MAXPATHLEN]; + + clilen = sizeof (cliaddr); + connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); + + /* + * After getting its ident string, tell client to get lost. + */ + if (get_client_ident(connfd, NULL, NULL, 0, NULL) == 0) { + (void) snprintf(nak, sizeof (nak), "%lu\n", + clientpid); + (void) write(connfd, nak, strlen(nak)); + } + (void) shutdown(connfd, SHUT_RDWR); + (void) close(connfd); +} + +static int +accept_socket(int servfd, pid_t verpid) +{ + int connfd; + struct sockaddr_un cliaddr; + socklen_t clilen = sizeof (cliaddr); + ucred_t *cred = NULL; + pid_t rpid = -1; + int flags; + + connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); + if (connfd == -1) + return (-1); + + /* Confirm connecting process is who we expect */ + if (getpeerucred(connfd, &cred) == 0) { + rpid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } + if (rpid == -1 || rpid != verpid) { + (void) shutdown(connfd, SHUT_RDWR); + (void) close(connfd); + return (-1); + } + + flags = fcntl(connfd, F_GETFL, 0); + if (flags != -1) + (void) fcntl(connfd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC); + + return (connfd); +} + +static void +ctlcmd_process(int sockfd, int stdoutfd, unsigned int *flags) +{ + char buf[BUFSIZ]; + int i; + for (i = 0; i < BUFSIZ-1; i++) { + char c; + if (read(sockfd, &c, 1) != 1 || + c == '\n' || c == '\0') { + break; + } + buf[i] = c; + } + if (i == 0) { + goto fail; + } + buf[i+1] = '\0'; + + if (strncmp(buf, "TIOCSWINSZ ", 11) == 0) { + char *next = buf + 11; + struct winsize ws; + errno = 0; + ws.ws_row = strtol(next, &next, 10); + if (errno == EINVAL) { + goto fail; + } + ws.ws_col = strtol(next + 1, &next, 10); + if (errno == EINVAL) { + goto fail; + } + if (ioctl(stdoutfd, TIOCSWINSZ, &ws) == 0) { + (void) write(sockfd, "OK\n", 3); + return; + } + } + if (strncmp(buf, "SETFLAGS ", 9) == 0) { + char *next = buf + 9; + unsigned int result; + errno = 0; + result = strtoul(next, &next, 10); + if (errno == EINVAL) { + goto fail; + } + *flags = result; + (void) write(sockfd, "OK\n", 3); + return; + } +fail: + (void) write(sockfd, "FAIL\n", 5); +} + +/* + * Check to see if the client at the other end of the socket is still alive; we + * know it is not if it throws EPIPE at us when we try to write an otherwise + * harmless 0-length message to it. + */ +static int +test_client(int clifd) +{ + if ((write(clifd, "", 0) == -1) && errno == EPIPE) + return (-1); + return (0); +} + +/* + * Modify the input string with json escapes. Since the destination can thus + * be larger than the source, it may get truncated, although we do use a + * larger buffer. + */ +static void +escape_json(char *sbuf, int slen, char *dbuf, int dlen) +{ + int i; + mbstate_t mbr; + wchar_t c; + size_t sz; + + bzero(&mbr, sizeof (mbr)); + + sbuf[slen] = '\0'; + i = 0; + while (i < dlen && (sz = mbrtowc(&c, sbuf, MB_CUR_MAX, &mbr)) > 0) { + switch (c) { + case '\\': + dbuf[i++] = '\\'; + dbuf[i++] = '\\'; + break; + + case '"': + dbuf[i++] = '\\'; + dbuf[i++] = '"'; + break; + + case '\b': + dbuf[i++] = '\\'; + dbuf[i++] = 'b'; + break; + + case '\f': + dbuf[i++] = '\\'; + dbuf[i++] = 'f'; + break; + + case '\n': + dbuf[i++] = '\\'; + dbuf[i++] = 'n'; + break; + + case '\r': + dbuf[i++] = '\\'; + dbuf[i++] = 'r'; + break; + + case '\t': + dbuf[i++] = '\\'; + dbuf[i++] = 't'; + break; + + default: + if ((c >= 0x00 && c <= 0x1f) || + (c > 0x7f && c <= 0xffff)) { + + i += snprintf(&dbuf[i], (dlen - i), "\\u%04x", + (int)(0xffff & c)); + } else if (c >= 0x20 && c <= 0x7f) { + dbuf[i++] = 0xff & c; + } + + break; + } + sbuf += sz; + } + + if (i == dlen) + dbuf[--i] = '\0'; + else + dbuf[i] = '\0'; +} + +/* + * We output to the log file as json. + * ex. for string 'msg\n' on the zone's stdout: + * {"log":"msg\n","stream":"stdout","time":"2014-10-24T20:12:11.101973117Z"} + * + * We use ns in the last field of the timestamp for compatability. + */ +static void +wr_log_msg(char *buf, int len, int from) +{ + struct timeval tv; + int olen; + char ts[64]; + char nbuf[BUFSIZ * 2]; + char obuf[BUFSIZ * 2]; + + escape_json(buf, len, nbuf, sizeof (nbuf)); + + if (gettimeofday(&tv, NULL) != 0) + return; + (void) strftime(ts, sizeof (ts), "%FT%T", gmtime(&tv.tv_sec)); + + olen = snprintf(obuf, sizeof (obuf), + "{\"log\":\"%s\",\"stream\":\"%s\",\"time\":\"%s.%ldZ\"}\n", + nbuf, (from == 1) ? "stdout" : "stderr", ts, tv.tv_usec * 1000); + + (void) write(logfd, obuf, olen); +} + +/* + * We want to sleep for a little while but need to be responsive if the zone is + * halting. We poll/sleep on the event stream so we can notice if we're halting. + * Return true if halting, otherwise false. + */ +static boolean_t +halt_sleep(int slptime) +{ + struct pollfd evfd[1]; + + evfd[0].fd = eventstream[1]; + evfd[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + if (poll(evfd, 1, slptime) > 0) { + /* zone halting */ + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * This routine drives the logging and interactive I/O loop. It polls for + * input from the zone side of the fd (output to stdout/stderr), and from the + * client (input to the zone's stdin). Additionally, it polls on the server + * fd, and disconnects any clients that might try to hook up with the zone + * while the fd's are in use. + * + * Data from the zone's stdout and stderr is formatted in json and written to + * the log file whether an interactive client is connected or not. + * + * When the client first calls us up, it is expected to send a line giving its + * "identity"; this consists of the string 'IDENT <pid> <locale>'. This is so + * that we can report that the fd's are busy, along with some diagnostics + * about who has them busy; the locale is ignore here but kept for compatability + * with the zlogin code when running on the zone's console. + * + * We need to handle the case where there is no server within the zone (or + * the server gets stuck) and data that we're writing to the zone server's + * stdin fills the pipe. Because of the way the zfd device works writes can + * flow into the stream and simply be dropped, if there is no server, or writes + * could return -1 with EAGAIN if the server is stuck. Since we ignore errors + * on the write to stdin, we won't get blocked in that case but we'd like to + * avoid dropping initial input if the server within the zone hasn't started + * yet. To handle this we wait to read initial input until we detect that there + * is a server inside the zone. We have to poll for this so that we can + * re-run the ioctl to notice when a server shows up. This poll/wait is handled + * by halt_sleep() so that we can be responsive if the zone wants to halt. + * We only do this check to avoid dropping initial input so it is possible for + * the server within the zone to go away later. At that point zfd will just + * drop any new input flowing into the stream. + */ +static void +do_zfd_io(int gzctlfd, int gzservfd, int gzerrfd, int stdinfd, int stdoutfd, + int stderrfd) +{ + struct pollfd pollfds[8]; + char ibuf[BUFSIZ + 1]; + int cc, ret; + int ctlfd = -1; + int clifd = -1; + int clierrfd = -1; + int pollerr = 0; + char clilocale[MAXPATHLEN]; + pid_t clipid = 0; + uint_t flags = 0; + boolean_t stdin_ready = B_FALSE; + int slptime = 250; /* initial poll sleep time in ms */ + + /* client control socket, watch for read events */ + pollfds[0].fd = ctlfd; + pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | + POLLPRI | POLLERR | POLLHUP | POLLNVAL; + + /* client socket, watch for read events */ + pollfds[1].fd = clifd; + pollfds[1].events = pollfds[0].events; + + /* stdout, watch for read events */ + pollfds[2].fd = stdoutfd; + pollfds[2].events = pollfds[0].events; + + /* stderr, watch for read events */ + pollfds[3].fd = stderrfd; + pollfds[3].events = pollfds[0].events; + + /* the server control socket; watch for new connections */ + pollfds[4].fd = gzctlfd; + pollfds[4].events = POLLIN | POLLRDNORM; + + /* the server stdin/out socket; watch for new connections */ + pollfds[5].fd = gzservfd; + pollfds[5].events = POLLIN | POLLRDNORM; + + /* the server stderr socket; watch for new connections */ + pollfds[6].fd = gzerrfd; + pollfds[6].events = POLLIN | POLLRDNORM; + + /* the eventstream; any input means the zone is halting */ + pollfds[7].fd = eventstream[1]; + pollfds[7].events = pollfds[0].events; + + while (!shutting_down) { + pollfds[0].revents = pollfds[1].revents = 0; + pollfds[2].revents = pollfds[3].revents = 0; + pollfds[4].revents = pollfds[5].revents = 0; + pollfds[6].revents = pollfds[7].revents = 0; + + ret = poll(pollfds, 8, -1); + if (ret == -1 && errno != EINTR) { + zerror(zlogp, B_TRUE, "poll failed"); + /* we are hosed, close connection */ + break; + } + + /* control events from client */ + if (pollfds[0].revents & + (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { + /* process control message */ + ctlcmd_process(ctlfd, stdoutfd, &flags); + } else if (pollfds[0].revents) { + /* bail if any error occurs */ + pollerr = pollfds[0].revents; + zerror(zlogp, B_FALSE, "closing connection " + "with control channel, pollerr %d\n", pollerr); + break; + } + + /* event from client side */ + if (pollfds[1].revents) { + if (stdin_ready) { + if (pollfds[1].revents & (POLLIN | + POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(clifd, ibuf, BUFSIZ); + if (cc > 0) { + /* + * See comment for this + * function on what happens if + * there is no reader in the + * zone. EOF is handled below. + */ + (void) write(stdinfd, ibuf, cc); + } + } else if (pollfds[1].revents & (POLLERR | + POLLNVAL)) { + pollerr = pollfds[1].revents; + zerror(zlogp, B_FALSE, + "closing connection " + "with client, pollerr %d\n", + pollerr); + break; + } + + if (pollfds[1].revents & POLLHUP) { + if (flags & ZLOGIN_ZFD_EOF) { + /* + * Let the client know. We've + * already serviced any pending + * regular input. Let the + * stream clear since the EOF + * ioctl jumps to the head. + */ + (void) ioctl(stdinfd, I_FLUSH); + if (halt_sleep(250)) + break; + (void) ioctl(stdinfd, ZFD_EOF); + } + break; + } + } else { + if (ioctl(stdinfd, ZFD_HAS_SLAVE) == 0) { + stdin_ready = B_TRUE; + } else { + /* + * There is nothing in the zone to read + * our input. Presumably the user + * providing input expects something to + * show up, but that is no guarantee. + * Since we haven't serviced the pending + * input poll yet, we don't want to + * immediately loop around but we also + * need to be responsive if the zone is + * halting. + */ + if (halt_sleep(slptime)) + break; + + if (slptime < 5000) + slptime += 250; + } + } + } + + /* event from the zone's stdout */ + if (pollfds[2].revents) { + if (pollfds[2].revents & + (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(stdoutfd, ibuf, BUFSIZ); + if (cc <= 0 && (errno != EINTR) && + (errno != EAGAIN)) + break; + if (cc > 0) { + wr_log_msg(ibuf, cc, 1); + + /* + * Lose output if no one is listening, + * otherwise pass it on. + */ + if (clifd != -1) + (void) write(clifd, ibuf, cc); + } + } else { + pollerr = pollfds[2].revents; + zerror(zlogp, B_FALSE, + "closing connection with stdout zfd, " + "pollerr %d\n", pollerr); + break; + } + } + + /* event from the zone's stderr */ + if (pollfds[3].revents) { + if (pollfds[3].revents & + (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(stderrfd, ibuf, BUFSIZ); + if (cc <= 0 && (errno != EINTR) && + (errno != EAGAIN)) + break; + if (cc > 0) { + wr_log_msg(ibuf, cc, 2); + + /* + * Lose output if no one is listening, + * otherwise pass it on. + */ + if (clierrfd != -1) + (void) write(clierrfd, ibuf, + cc); + } + } else { + pollerr = pollfds[3].revents; + zerror(zlogp, B_FALSE, + "closing connection with stderr zfd, " + "pollerr %d\n", pollerr); + break; + } + } + + /* connect event from server control socket */ + if (pollfds[4].revents) { + if (ctlfd != -1) { + /* + * Test the client to see if it is really + * still alive. If it has died but we + * haven't yet detected that, we might + * deny a legitimate connect attempt. If it + * is dead, we break out; once we tear down + * the old connection, the new connection + * will happen. + */ + if (test_client(ctlfd) == -1) { + break; + } + /* we're already handling a client */ + reject_client(gzctlfd, clipid); + } else { + ctlfd = accept_client(gzctlfd, &clipid, + clilocale, sizeof (clilocale), &flags); + if (ctlfd != -1) { + pollfds[0].fd = ctlfd; + } else { + break; + } + } + } + + /* connect event from server stdin/out socket */ + if (pollfds[5].revents) { + if (ctlfd == -1) { + /* + * This shouldn't happen since the client is + * expected to connect on the control socket + * first. If we see this, tear everything down + * and start over. + */ + zerror(zlogp, B_FALSE, "GZ zfd stdin/stdout " + "connection attempt with no GZ control\n"); + break; + } + assert(clifd == -1); + if ((clifd = accept_socket(gzservfd, clipid)) != -1) { + /* No need to watch for other new connections */ + pollfds[5].fd = -1; + /* Client input is of interest, though */ + pollfds[1].fd = clifd; + } else { + break; + } + } + + /* connection event from server stderr socket */ + if (pollfds[6].revents) { + if (ctlfd == -1) { + /* + * Same conditions apply to stderr as stdin/out. + */ + zerror(zlogp, B_FALSE, "GZ zfd stderr " + "connection attempt with no GZ control\n"); + break; + } + assert(clierrfd == -1); + if ((clierrfd = accept_socket(gzerrfd, clipid)) != -1) { + /* No need to watch for other new connections */ + pollfds[6].fd = -1; + } else { + break; + } + } + + /* + * Watch for events on the eventstream. This is how we get + * notified of the zone halting, etc. It provides us a + * "wakeup" from poll when important things happen, which + * is good. + */ + if (pollfds[7].revents) { + break; + } + } + + if (clifd != -1) { + (void) shutdown(clifd, SHUT_RDWR); + (void) close(clifd); + } + + if (clierrfd != -1) { + (void) shutdown(clierrfd, SHUT_RDWR); + (void) close(clierrfd); + } +} + +static int +open_fd(zlog_t *zlogp, int id, int rw) +{ + int fd; + int flag = O_NONBLOCK | O_NOCTTY | O_CLOEXEC; + int retried = 0; + char stdpath[MAXPATHLEN]; + + (void) snprintf(stdpath, sizeof (stdpath), "/dev/zfd/%s/master/%d", + zone_name, id); + flag |= rw; + + while (!shutting_down) { + if ((fd = open(stdpath, flag)) != -1) { + /* + * Setting RPROTDIS on the stream means that the + * control portion of messages received (which we don't + * care about) will be discarded by the stream head. If + * we allowed such messages, we wouldn't be able to use + * read(2), as it fails (EBADMSG) when a message with a + * control element is received. + */ + if (ioctl(fd, I_SRDOPT, RNORM|RPROTDIS) == -1) { + zerror(zlogp, B_TRUE, + "failed to set options on zfd"); + return (-1); + } + return (fd); + } + + if (retried++ > 60) + break; + + (void) sleep(1); + } + + zerror(zlogp, B_TRUE, "failed to open zfd"); + return (-1); +} + +static void +open_logfile() +{ + char logpath[MAXPATHLEN]; + + logfd = -1; + + (void) snprintf(logpath, sizeof (logpath), "%s/logs", zonepath); + (void) mkdir(logpath, 0700); + + (void) snprintf(logpath, sizeof (logpath), "%s/logs/%s", zonepath, + LOGNAME); + + if ((logfd = open(logpath, O_WRONLY | O_APPEND | O_CREAT, 0600)) == -1) + zerror(zlogp, B_TRUE, "failed to open log file"); +} + +/* ARGSUSED */ +void +hup_handler(int i) +{ + (void) close(logfd); + open_logfile(); +} + +/* + * Body of the worker thread to log the zfd's stdout and stderr to a log file + * and to perform interactive IO to the stdin, stdout and stderr zfd's. + * + * The stdin, stdout and stderr are from the perspective of the process inside + * the zone, so the zoneadmd view is opposite (i.e. we write to the stdin fd + * and read from the stdout/stderr fds). + */ +static void +srvr(void *modearg) +{ + zlog_mode_t mode = (zlog_mode_t)modearg; + int gzctlfd = -1; + int gzoutfd = -1; + int stdinfd = -1; + int stdoutfd = -1; + sigset_t blockset; + int gzerrfd = -1; + int stderrfd = -1; + + if (!shutting_down) { + open_logfile(); + } + + /* + * This thread should receive SIGHUP so that it can close the log + * file, and reopen it, during log rotation. + */ + sigset(SIGHUP, hup_handler); + (void) sigfillset(&blockset); + (void) sigdelset(&blockset, SIGHUP); + (void) thr_sigsetmask(SIG_BLOCK, &blockset, NULL); + + if (!shutting_down) { + if (pipe(eventstream) != 0) { + zerror(zlogp, B_TRUE, "failed to open logger control " + "pipe"); + return; + } + } + + while (!shutting_down) { + if (init_server_sock(zlogp, &gzctlfd, "ctl") == -1) { + zerror(zlogp, B_FALSE, + "server setup: control socket init failed"); + goto death; + } + if (init_server_sock(zlogp, &gzoutfd, "out") == -1) { + zerror(zlogp, B_FALSE, + "server setup: stdout socket init failed"); + goto death; + } + if (init_server_sock(zlogp, &gzerrfd, "err") == -1) { + zerror(zlogp, B_FALSE, + "server setup: stderr socket init failed"); + goto death; + } + + if (mode == ZLOG_INTERACTIVE) { + if ((stdinfd = open_fd(zlogp, 0, O_RDWR)) == -1) { + goto death; + } + stdoutfd = stdinfd; + } else { + if ((stdinfd = open_fd(zlogp, 0, O_WRONLY)) == -1 || + (stdoutfd = open_fd(zlogp, 1, O_RDONLY)) == -1 || + (stderrfd = open_fd(zlogp, 2, O_RDONLY)) == -1) { + goto death; + } + } + + do_zfd_io(gzctlfd, gzoutfd, gzerrfd, stdinfd, stdoutfd, + stderrfd); +death: + destroy_server_sock(gzctlfd, "ctl"); + destroy_server_sock(gzoutfd, "out"); + destroy_server_sock(gzerrfd, "err"); + (void) close(stdinfd); + if (mode != ZLOG_INTERACTIVE) { + (void) close(stdoutfd); + (void) close(stderrfd); + } + } + + (void) close(eventstream[0]); + eventstream[0] = -1; + (void) close(eventstream[1]); + eventstream[1] = -1; + (void) close(logfd); +} + +static zlog_mode_t +get_logger_mode() +{ + zlog_mode_t mode = ZLOG_NONE; + zone_dochandle_t handle; + struct zone_attrtab attr; + + if ((handle = zonecfg_init_handle()) == NULL) + return (mode); + + if (zonecfg_get_handle(zone_name, handle) != Z_OK) + goto done; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp(ZLOG_MODE, attr.zone_attr_name) == 0) { + if (strncmp("log", attr.zone_attr_value, 3) == 0) { + mode = ZLOG_LOG; + } else if (strncmp("int", + attr.zone_attr_value, 3) == 0) { + mode = ZLOG_INTERACTIVE; + } + break; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); + return (mode); +} + +void +create_log_thread(zlog_t *logp, zoneid_t id) +{ + int res; + zlog_mode_t mode; + + shutting_down = 0; + zlogp = logp; + + mode = get_logger_mode(); + if (mode == ZLOG_NONE) + return; + + if (init_zfd_devs(zlogp, mode) == -1) { + zerror(zlogp, B_FALSE, + "zfd setup: device initialization failed"); + return; + } + + res = thr_create(NULL, 0, (void * (*)(void *))srvr, (void *)mode, 0, + &logger_tid); + if (res != 0) { + zerror(zlogp, B_FALSE, "error %d creating logger thread", res); + logger_tid = 0; + } +} + +void +destroy_log_thread() +{ + if (logger_tid != 0) { + int stop = 1; + + shutting_down = 1; + /* break out of poll to shutdown */ + if (eventstream[0] != -1) + (void) write(eventstream[0], &stop, sizeof (stop)); + (void) thr_join(logger_tid, NULL, NULL); + logger_tid = 0; + } + + (void) destroy_zfd_devs(zlogp); +} diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c index cb81b77727..72c14bc9ff 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.c +++ b/usr/src/cmd/zoneadmd/zoneadmd.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014, Joyent, Inc. All rights reserved. */ /* @@ -68,6 +69,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/sysmacros.h> +#include <sys/time.h> #include <bsm/adt.h> #include <bsm/adt_event.h> @@ -108,6 +110,8 @@ static char *progname; char *zone_name; /* zone which we are managing */ +zone_dochandle_t snap_hndl; /* handle for snapshot created when ready */ +char zonepath[MAXNAMELEN]; char pool_name[MAXNAMELEN]; char default_brand[MAXNAMELEN]; char brand_name[MAXNAMELEN]; @@ -116,10 +120,11 @@ boolean_t zone_iscluster; boolean_t zone_islabeled; boolean_t shutdown_in_progress; static zoneid_t zone_id; +static zoneid_t zone_did = 0; dladm_handle_t dld_handle = NULL; -static char pre_statechg_hook[2 * MAXPATHLEN]; -static char post_statechg_hook[2 * MAXPATHLEN]; +char pre_statechg_hook[2 * MAXPATHLEN]; +char post_statechg_hook[2 * MAXPATHLEN]; char query_hook[2 * MAXPATHLEN]; zlog_t logsys; @@ -141,6 +146,9 @@ boolean_t bringup_failure_recovery = B_FALSE; /* ignore certain failures */ #define DEFAULT_LOCALE "C" +#define RSRC_NET "net" +#define RSRC_DEV "device" + static const char * z_cmd_name(zone_cmd_t zcmd) { @@ -257,34 +265,31 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...) } /* - * Emit a warning for any boot arguments which are unrecognized. Since - * Solaris boot arguments are getopt(3c) compatible (see kernel(1m)), we + * Since Solaris boot arguments are getopt(3c) compatible (see kernel(1m)), we * put the arguments into an argv style array, use getopt to process them, - * and put the resultant argument string back into outargs. + * and put the resultant argument string back into outargs. Non-Solaris brands + * may support alternate forms of boot arguments so we must handle that as well. * * During the filtering, we pull out any arguments which are truly "boot" * arguments, leaving only those which are to be passed intact to the * progenitor process. The one we support at the moment is -i, which * indicates to the kernel which program should be launched as 'init'. * - * A return of Z_INVAL indicates specifically that the arguments are - * not valid; this is a non-fatal error. Except for Z_OK, all other return - * values are treated as fatal. + * Except for Z_OK, all other return values are treated as fatal. */ static int filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, - char *init_file, char *badarg) + char *init_file) { int argc = 0, argc_save; int i; - int err; + int err = Z_OK; char *arg, *lasts, **argv = NULL, **argv_save; char zonecfg_args[BOOTARGS_MAX]; char scratchargs[BOOTARGS_MAX], *sargs; char c; bzero(outargs, BOOTARGS_MAX); - bzero(badarg, BOOTARGS_MAX); /* * If the user didn't specify transient boot arguments, check @@ -292,25 +297,10 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, * and use them if applicable. */ if (inargs == NULL || inargs[0] == '\0') { - zone_dochandle_t handle; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, - "getting zone configuration handle"); - return (Z_BAD_HANDLE); - } - err = zonecfg_get_snapshot_handle(zone_name, handle); - if (err != Z_OK) { - zerror(zlogp, B_FALSE, - "invalid configuration snapshot"); - zonecfg_fini_handle(handle); - return (Z_BAD_HANDLE); - } - bzero(zonecfg_args, sizeof (zonecfg_args)); - (void) zonecfg_get_bootargs(handle, zonecfg_args, + (void) zonecfg_get_bootargs(snap_hndl, zonecfg_args, sizeof (zonecfg_args)); inargs = zonecfg_args; - zonecfg_fini_handle(handle); } if (strlen(inargs) >= BOOTARGS_MAX) { @@ -390,36 +380,29 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, break; case '?': /* - * We warn about unknown arguments but pass them - * along anyway-- if someone wants to develop their - * own init replacement, they can pass it whatever - * args they want. + * If a brand has its own init, we need to pass along + * whatever the user provides. We use the entire + * unknown string here so that we correctly handle + * unknown long options (e.g. --debug). */ - err = Z_INVAL; (void) snprintf(outargs, BOOTARGS_MAX, - "%s -%c", outargs, optopt); - (void) snprintf(badarg, BOOTARGS_MAX, - "%s -%c", badarg, optopt); + "%s %s", outargs, argv[optind - 1]); break; } } /* - * For Solaris Zones we warn about and discard non-option arguments. - * Hence 'boot foo bar baz gub' --> 'boot'. However, to be similar - * to the kernel, we concat up all the other remaining boot args. - * and warn on them as a group. + * We need to pass along everything else since we don't know what + * the brand's init is expecting. For example, an argument list like: + * --confdir /foo --debug + * will cause the getopt parsing to stop at '/foo' but we need to pass + * that on, along with the '--debug'. This does mean that we require + * any of our known options (-ifms) to preceed the brand-specific ones. */ - if (optind < argc) { - err = Z_INVAL; - while (optind < argc) { - (void) snprintf(badarg, BOOTARGS_MAX, "%s%s%s", - badarg, strlen(badarg) > 0 ? " " : "", - argv[optind]); - optind++; - } - zerror(zlogp, B_FALSE, "WARNING: Unused or invalid boot " - "arguments `%s'.", badarg); + while (optind < argc) { + (void) snprintf(outargs, BOOTARGS_MAX, "%s %s", outargs, + argv[optind]); + optind++; } done: @@ -458,7 +441,7 @@ mkzonedir(zlog_t *zlogp) * Run the brand's pre-state change callback, if it exists. */ static int -brand_prestatechg(zlog_t *zlogp, int state, int cmd) +brand_prestatechg(zlog_t *zlogp, int state, int cmd, boolean_t debug) { char cmdbuf[2 * MAXPATHLEN]; const char *altroot; @@ -471,7 +454,7 @@ brand_prestatechg(zlog_t *zlogp, int state, int cmd) state, cmd, altroot) > sizeof (cmdbuf)) return (-1); - if (do_subproc(zlogp, cmdbuf, NULL) != 0) + if (do_subproc(zlogp, cmdbuf, NULL, debug) != 0) return (-1); return (0); @@ -481,7 +464,7 @@ brand_prestatechg(zlog_t *zlogp, int state, int cmd) * Run the brand's post-state change callback, if it exists. */ static int -brand_poststatechg(zlog_t *zlogp, int state, int cmd) +brand_poststatechg(zlog_t *zlogp, int state, int cmd, boolean_t debug) { char cmdbuf[2 * MAXPATHLEN]; const char *altroot; @@ -494,7 +477,7 @@ brand_poststatechg(zlog_t *zlogp, int state, int cmd) state, cmd, altroot) > sizeof (cmdbuf)) return (-1); - if (do_subproc(zlogp, cmdbuf, NULL) != 0) + if (do_subproc(zlogp, cmdbuf, NULL, debug) != 0) return (-1); return (0); @@ -533,35 +516,44 @@ notify_zonestatd(zoneid_t zoneid) * subcommand. */ static int -zone_ready(zlog_t *zlogp, zone_mnt_t mount_cmd, int zstate) +zone_ready(zlog_t *zlogp, zone_mnt_t mount_cmd, int zstate, boolean_t debug) { int err; + boolean_t snapped = B_FALSE; - if (brand_prestatechg(zlogp, zstate, Z_READY) != 0) - return (-1); - + if ((snap_hndl = zonecfg_init_handle()) == NULL) { + zerror(zlogp, B_TRUE, "getting zone configuration handle"); + goto bad; + } if ((err = zonecfg_create_snapshot(zone_name)) != Z_OK) { zerror(zlogp, B_FALSE, "unable to create snapshot: %s", zonecfg_strerror(err)); goto bad; } + snapped = B_TRUE; - if ((zone_id = vplat_create(zlogp, mount_cmd)) == -1) { - if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) - zerror(zlogp, B_FALSE, "destroying snapshot: %s", - zonecfg_strerror(err)); + if (zonecfg_get_snapshot_handle(zone_name, snap_hndl) != Z_OK) { + zerror(zlogp, B_FALSE, "invalid configuration snapshot"); goto bad; } + + if (zone_did == 0) + zone_did = zone_get_did(zone_name); + + if (brand_prestatechg(zlogp, zstate, Z_READY, debug) != 0) + goto bad; + + if ((zone_id = vplat_create(zlogp, mount_cmd, zone_did)) == -1) + goto bad; + if (vplat_bringup(zlogp, mount_cmd, zone_id) != 0) { bringup_failure_recovery = B_TRUE; - (void) vplat_teardown(NULL, (mount_cmd != Z_MNT_BOOT), B_FALSE); - if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) - zerror(zlogp, B_FALSE, "destroying snapshot: %s", - zonecfg_strerror(err)); + (void) vplat_teardown(NULL, (mount_cmd != Z_MNT_BOOT), B_FALSE, + debug); goto bad; } - if (brand_poststatechg(zlogp, zstate, Z_READY) != 0) + if (brand_poststatechg(zlogp, zstate, Z_READY, debug) != 0) goto bad; return (0); @@ -571,7 +563,13 @@ bad: * If something goes wrong, we up the zones's state to the target * state, READY, and then invoke the hook as if we're halting. */ - (void) brand_poststatechg(zlogp, ZONE_STATE_READY, Z_HALT); + (void) brand_poststatechg(zlogp, ZONE_STATE_READY, Z_HALT, debug); + if (snapped) + if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) + zerror(zlogp, B_FALSE, "destroying snapshot: %s", + zonecfg_strerror(err)); + zonecfg_fini_handle(snap_hndl); + snap_hndl = NULL; return (-1); } @@ -623,15 +621,8 @@ mount_early_fs(void *data, const char *spec, const char *dir, /* determine the zone rootpath */ if (mount_cmd) { - char zonepath[MAXPATHLEN]; char luroot[MAXPATHLEN]; - if (zone_get_zonepath(zone_name, - zonepath, sizeof (zonepath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - return (-1); - } - (void) snprintf(luroot, sizeof (luroot), "%s/lu", zonepath); resolve_lofs(zlogp, luroot, sizeof (luroot)); (void) strlcpy(rootpath, luroot, sizeof (rootpath)); @@ -686,6 +677,8 @@ mount_early_fs(void *data, const char *spec, const char *dir, char opt_buf[MAX_MNTOPT_STR]; int optlen = 0; int mflag = MS_DATA; + int i; + int ret; (void) ct_tmpl_clear(tmpl_fd); /* @@ -713,9 +706,26 @@ mount_early_fs(void *data, const char *spec, const char *dir, optlen = MAX_MNTOPT_STR; mflag = MS_OPTIONSTR; } - if (mount(spec, dir, mflag, fstype, NULL, 0, opt, optlen) != 0) - _exit(errno); - _exit(0); + + /* + * There is an obscure race condition which can cause mount + * to return EBUSY. This happens for example on the mount + * of the zone's /etc/svc/volatile file system if there is + * a GZ process running svcs -Z, which will touch the + * mountpoint, just as we're trying to do the mount. To cope + * with this, we retry up to 3 times to let this transient + * process get out of the way. + */ + for (i = 0; i < 3; i++) { + ret = 0; + if (mount(spec, dir, mflag, fstype, NULL, 0, opt, + optlen) != 0) + ret = errno; + if (ret != EBUSY) + break; + (void) sleep(1); + } + _exit(ret); } /* parent */ @@ -739,12 +749,150 @@ mount_early_fs(void *data, const char *spec, const char *dir, } /* + * env variable name format + * _ZONECFG_{resource name}_{identifying attr. name}_{property name} + * Any dashes (-) in the property names are replaced with underscore (_). + */ +static void +set_zonecfg_env(char *rsrc, char *attr, char *name, char *val) +{ + char *p; + char nm[MAXNAMELEN]; + + if (attr == NULL) + (void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s", rsrc, + name); + else + (void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s_%s", rsrc, + attr, name); + + p = nm; + while ((p = strchr(p, '-')) != NULL) + *p++ = '_'; + + (void) setenv(nm, val, 1); +} + +/* + * Export zonecfg network and device properties into environment for the boot + * and state change hooks. + * If debug is true, export the brand hook debug env. variable as well. + * + * We could export more of the config in the future, as necessary. + */ +static int +setup_subproc_env(boolean_t debug) +{ + int res; + struct zone_nwiftab ntab; + struct zone_devtab dtab; + struct zone_attrtab atab; + char net_resources[MAXNAMELEN * 2]; + char dev_resources[MAXNAMELEN * 2]; + + /* snap_hndl is null when called through the set_brand_env code path */ + if (snap_hndl == NULL) + return (Z_OK); + + net_resources[0] = '\0'; + if ((res = zonecfg_setnwifent(snap_hndl)) != Z_OK) + goto done; + + while (zonecfg_getnwifent(snap_hndl, &ntab) == Z_OK) { + struct zone_res_attrtab *rap; + char *phys; + + phys = ntab.zone_nwif_physical; + + (void) strlcat(net_resources, phys, sizeof (net_resources)); + (void) strlcat(net_resources, " ", sizeof (net_resources)); + + set_zonecfg_env(RSRC_NET, phys, "physical", phys); + + set_zonecfg_env(RSRC_NET, phys, "address", + ntab.zone_nwif_address); + set_zonecfg_env(RSRC_NET, phys, "allowed-address", + ntab.zone_nwif_allowed_address); + set_zonecfg_env(RSRC_NET, phys, "defrouter", + ntab.zone_nwif_defrouter); + set_zonecfg_env(RSRC_NET, phys, "global-nic", + ntab.zone_nwif_gnic); + set_zonecfg_env(RSRC_NET, phys, "mac-addr", ntab.zone_nwif_mac); + set_zonecfg_env(RSRC_NET, phys, "vlan-id", + ntab.zone_nwif_vlan_id); + + for (rap = ntab.zone_nwif_attrp; rap != NULL; + rap = rap->zone_res_attr_next) + set_zonecfg_env(RSRC_NET, phys, rap->zone_res_attr_name, + rap->zone_res_attr_value); + nwifent_free_attrs(&ntab); + } + + (void) setenv("_ZONECFG_net_resources", net_resources, 1); + + (void) zonecfg_endnwifent(snap_hndl); + + if ((res = zonecfg_setdevent(snap_hndl)) != Z_OK) + goto done; + + while (zonecfg_getdevent(snap_hndl, &dtab) == Z_OK) { + struct zone_res_attrtab *rap; + char *match; + + match = dtab.zone_dev_match; + + (void) strlcat(dev_resources, match, sizeof (dev_resources)); + (void) strlcat(dev_resources, " ", sizeof (dev_resources)); + + for (rap = dtab.zone_dev_attrp; rap != NULL; + rap = rap->zone_res_attr_next) + set_zonecfg_env(RSRC_DEV, match, + rap->zone_res_attr_name, rap->zone_res_attr_value); + } + + (void) zonecfg_enddevent(snap_hndl); + + if ((res = zonecfg_setattrent(snap_hndl)) != Z_OK) + goto done; + + while (zonecfg_getattrent(snap_hndl, &atab) == Z_OK) { + set_zonecfg_env("attr", NULL, atab.zone_attr_name, + atab.zone_attr_value); + } + + (void) zonecfg_endattrent(snap_hndl); + + if (debug) + (void) setenv("_ZONEADMD_brand_debug", "1", 1); + else + (void) setenv("_ZONEADMD_brand_debug", "", 1); + + res = Z_OK; + +done: + return (res); +} + +void +nwifent_free_attrs(struct zone_nwiftab *np) +{ + struct zone_res_attrtab *rap; + + for (rap = np->zone_nwif_attrp; rap != NULL; ) { + struct zone_res_attrtab *tp = rap; + + rap = rap->zone_res_attr_next; + free(tp); + } +} + +/* * If retstr is not NULL, the output of the subproc is returned in the str, * otherwise it is output using zerror(). Any memory allocated for retstr * should be freed by the caller. */ int -do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) +do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr, boolean_t debug) { char buf[1024]; /* arbitrary large amount */ char *inbuf; @@ -763,6 +911,11 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) inbuf = buf; } + if (setup_subproc_env(debug) != Z_OK) { + zerror(zlogp, B_FALSE, "failed to setup environment"); + return (-1); + } + file = popen(cmdbuf, "r"); if (file == NULL) { zerror(zlogp, B_TRUE, "could not launch: %s", cmdbuf); @@ -771,8 +924,13 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) while (fgets(inbuf, 1024, file) != NULL) { if (retstr == NULL) { - if (zlogp != &logsys) + if (zlogp != &logsys) { + int last = strlen(inbuf) - 1; + + if (inbuf[last] == '\n') + inbuf[last] = '\0'; zerror(zlogp, B_FALSE, "%s", inbuf); + } } else { char *p; @@ -802,24 +960,66 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) return (WEXITSTATUS(status)); } +/* + * Get the path for this zone's init(1M) (or equivalent) process. First look + * for a zone-specific init-name attr, then get it from the brand. + */ static int -zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) +get_initname(brand_handle_t bh, char *initname, int len) +{ + struct zone_attrtab a; + + bzero(&a, sizeof (a)); + (void) strlcpy(a.zone_attr_name, "init-name", + sizeof (a.zone_attr_name)); + + if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK) { + (void) strlcpy(initname, a.zone_attr_value, len); + return (0); + } + + return (brand_get_initname(bh, initname, len)); +} + +/* + * Get the restart-init flag for this zone's init(1M) (or equivalent) process. + * First look for a zone-specific restart-init attr, then get it from the brand. + */ +static boolean_t +restartinit(brand_handle_t bh) +{ + struct zone_attrtab a; + + bzero(&a, sizeof (a)); + (void) strlcpy(a.zone_attr_name, "restart-init", + sizeof (a.zone_attr_name)); + + if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK) { + if (strcmp(a.zone_attr_value, "false") == 0) + return (B_FALSE); + return (B_TRUE); + } + + return (brand_restartinit(bh)); +} + +static int +zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate, boolean_t debug) { zoneid_t zoneid; struct stat st; - char zpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN]; + char rpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN]; char nbootargs[BOOTARGS_MAX]; char cmdbuf[MAXPATHLEN]; fs_callback_t cb; brand_handle_t bh; zone_iptype_t iptype; - boolean_t links_loaded = B_FALSE; dladm_status_t status; char errmsg[DLADM_STRSIZE]; int err; boolean_t restart_init; - if (brand_prestatechg(zlogp, zstate, Z_BOOT) != 0) + if (brand_prestatechg(zlogp, zstate, Z_BOOT, debug) != 0) return (-1); if ((zoneid = getzoneidbyname(zone_name)) == -1) { @@ -852,13 +1052,8 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) /* * Get the brand's boot callback if it exists. */ - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - brand_close(bh); - goto bad; - } (void) strcpy(cmdbuf, EXEC_PREFIX); - if (brand_get_boot(bh, zone_name, zpath, cmdbuf + EXEC_LEN, + if (brand_get_boot(bh, zone_name, zonepath, cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN) != 0) { zerror(zlogp, B_FALSE, "unable to determine branded zone's boot callback"); @@ -867,34 +1062,31 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) } /* Get the path for this zone's init(1M) (or equivalent) process. */ - if (brand_get_initname(bh, init_file, MAXPATHLEN) != 0) { + if (get_initname(bh, init_file, MAXPATHLEN) != 0) { zerror(zlogp, B_FALSE, "unable to determine zone's init(1M) location"); brand_close(bh); goto bad; } - /* See if this zone's brand should restart init if it dies. */ - restart_init = brand_restartinit(bh); + /* See if we should restart init if it dies. */ + restart_init = restartinit(bh); brand_close(bh); - err = filter_bootargs(zlogp, bootargs, nbootargs, init_file, - bad_boot_arg); - if (err == Z_INVAL) - eventstream_write(Z_EVT_ZONE_BADARGS); - else if (err != Z_OK) + err = filter_bootargs(zlogp, bootargs, nbootargs, init_file); + if (err != Z_OK) goto bad; assert(init_file[0] != '\0'); /* Try to anticipate possible problems: Make sure init is executable. */ - if (zone_get_rootpath(zone_name, zpath, sizeof (zpath)) != Z_OK) { + if (zone_get_rootpath(zone_name, rpath, sizeof (rpath)) != Z_OK) { zerror(zlogp, B_FALSE, "unable to determine zone root"); goto bad; } - (void) snprintf(initpath, sizeof (initpath), "%s%s", zpath, init_file); + (void) snprintf(initpath, sizeof (initpath), "%s%s", rpath, init_file); if (stat(initpath, &st) == -1) { zerror(zlogp, B_TRUE, "could not stat %s", initpath); @@ -919,7 +1111,6 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) " %s", dladm_status2str(status, errmsg)); goto bad; } - links_loaded = B_TRUE; } /* @@ -928,7 +1119,7 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) * is booted. */ if ((strlen(cmdbuf) > EXEC_LEN) && - (do_subproc(zlogp, cmdbuf, NULL) != Z_OK)) { + (do_subproc(zlogp, cmdbuf, NULL, debug) != Z_OK)) { zerror(zlogp, B_FALSE, "%s failed", cmdbuf); goto bad; } @@ -960,9 +1151,15 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) goto bad; } - if (brand_poststatechg(zlogp, zstate, Z_BOOT) != 0) + if (brand_poststatechg(zlogp, zstate, Z_BOOT, debug) != 0) goto bad; + /* Startup a thread to perform zfd logging/tty svc for the zone. */ + create_log_thread(zlogp, zone_id); + + /* Startup a thread to perform memory capping for the zone. */ + create_mcap_thread(zlogp, zone_id); + return (0); bad: @@ -970,32 +1167,42 @@ bad: * If something goes wrong, we up the zones's state to the target * state, RUNNING, and then invoke the hook as if we're halting. */ - (void) brand_poststatechg(zlogp, ZONE_STATE_RUNNING, Z_HALT); - if (links_loaded) - (void) dladm_zone_halt(dld_handle, zoneid); + (void) brand_poststatechg(zlogp, ZONE_STATE_RUNNING, Z_HALT, debug); + return (-1); } static int -zone_halt(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, int zstate) +zone_halt(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, int zstate, + boolean_t debug) { int err; - if (brand_prestatechg(zlogp, zstate, Z_HALT) != 0) + if (brand_prestatechg(zlogp, zstate, Z_HALT, debug) != 0) return (-1); - if (vplat_teardown(zlogp, unmount_cmd, rebooting) != 0) { + /* Shutting down, stop the memcap thread */ + destroy_mcap_thread(); + + if (vplat_teardown(zlogp, unmount_cmd, rebooting, debug) != 0) { if (!bringup_failure_recovery) zerror(zlogp, B_FALSE, "unable to destroy zone"); + destroy_log_thread(); return (-1); } + /* Shut down is done, stop the log thread */ + destroy_log_thread(); + + if (brand_poststatechg(zlogp, zstate, Z_HALT, debug) != 0) + return (-1); + if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) zerror(zlogp, B_FALSE, "destroying snapshot: %s", zonecfg_strerror(err)); - if (brand_poststatechg(zlogp, zstate, Z_HALT) != 0) - return (-1); + zonecfg_fini_handle(snap_hndl); + snap_hndl = NULL; return (0); } @@ -1007,7 +1214,6 @@ zone_graceful_shutdown(zlog_t *zlogp) pid_t child; char cmdbuf[MAXPATHLEN]; brand_handle_t bh = NULL; - char zpath[MAXPATHLEN]; ctid_t ct; int tmpl_fd; int child_status; @@ -1028,18 +1234,12 @@ zone_graceful_shutdown(zlog_t *zlogp) return (-1); } - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - brand_close(bh); - return (-1); - } - /* * If there is a brand 'shutdown' callback, execute it now to give the * brand a chance to cleanup any custom configuration. */ (void) strcpy(cmdbuf, EXEC_PREFIX); - if (brand_get_shutdown(bh, zone_name, zpath, cmdbuf + EXEC_LEN, + if (brand_get_shutdown(bh, zone_name, zonepath, cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN) != 0 || strlen(cmdbuf) <= EXEC_LEN) { (void) strcat(cmdbuf, SHUTDOWN_DEFAULT); } @@ -1177,6 +1377,36 @@ audit_put_record(zlog_t *zlogp, ucred_t *uc, int return_val, } /* + * Log the exit time and status of the zone's init process into + * {zonepath}/lastexited. If the zone shutdown normally, the exit status will + * be -1, otherwise it will be the exit status as described in wait.3c. + * If the zone is configured to restart init, then nothing will be logged if + * init exits unexpectedly (the kernel will never upcall in this case). + */ +static void +log_init_exit(int status) +{ + char p[MAXPATHLEN]; + char buf[128]; + struct timeval t; + int fd; + + if (snprintf(p, sizeof (p), "%s/lastexited", zonepath) > sizeof (p)) + return; + if (gettimeofday(&t, NULL) != 0) + return; + if (snprintf(buf, sizeof (buf), "%ld.%ld %d\n", t.tv_sec, t.tv_usec, + status) > sizeof (buf)) + return; + if ((fd = open(p, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) + return; + + (void) write(fd, buf, strlen(buf)); + + (void) close(fd); +} + +/* * The main routine for the door server that deals with zone state transitions. */ /* ARGSUSED */ @@ -1189,9 +1419,11 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, zone_state_t zstate; zone_cmd_t cmd; + boolean_t debug; + int init_status; zone_cmd_arg_t *zargp; - boolean_t kernelcall; + boolean_t kernelcall = B_TRUE; int rval = -1; uint64_t uniqid; @@ -1241,6 +1473,8 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, goto out; } cmd = zargp->cmd; + debug = zargp->debug; + init_status = zargp->status; if (door_ucred(&uc) != 0) { zerror(&logsys, B_TRUE, "door_ucred"); @@ -1347,23 +1581,25 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case ZONE_STATE_INSTALLED: switch (cmd) { case Z_READY: - rval = zone_ready(zlogp, Z_MNT_BOOT, zstate); + rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, debug); if (rval == 0) eventstream_write(Z_EVT_ZONE_READIED); + zcons_statechanged(); break; case Z_BOOT: case Z_FORCEBOOT: eventstream_write(Z_EVT_ZONE_BOOTING); - if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) - == 0) { + if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, + debug)) == 0) { rval = zone_bootup(zlogp, zargp->bootbuf, - zstate); + zstate, debug); } audit_put_record(zlogp, uc, rval, "boot"); + zcons_statechanged(); if (rval != 0) { bringup_failure_recovery = B_TRUE; (void) zone_halt(zlogp, B_FALSE, B_FALSE, - zstate); + zstate, debug); eventstream_write(Z_EVT_ZONE_BOOTFAILED); } break; @@ -1415,7 +1651,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = zone_ready(zlogp, strcmp(zargp->bootbuf, "-U") == 0 ? - Z_MNT_UPDATE : Z_MNT_SCRATCH, zstate); + Z_MNT_UPDATE : Z_MNT_SCRATCH, zstate, debug); if (rval != 0) break; @@ -1477,15 +1713,18 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = 0; break; case Z_BOOT: + case Z_FORCEBOOT: (void) strlcpy(boot_args, zargp->bootbuf, sizeof (boot_args)); eventstream_write(Z_EVT_ZONE_BOOTING); - rval = zone_bootup(zlogp, zargp->bootbuf, zstate); + rval = zone_bootup(zlogp, zargp->bootbuf, zstate, + debug); audit_put_record(zlogp, uc, rval, "boot"); + zcons_statechanged(); if (rval != 0) { bringup_failure_recovery = B_TRUE; (void) zone_halt(zlogp, B_FALSE, B_TRUE, - zstate); + zstate, debug); eventstream_write(Z_EVT_ZONE_BOOTFAILED); } boot_args[0] = '\0'; @@ -1493,15 +1732,17 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case Z_HALT: if (kernelcall) /* Invalid; can't happen */ abort(); - if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate)) - != 0) + if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate, + debug)) != 0) break; + zcons_statechanged(); eventstream_write(Z_EVT_ZONE_HALTED); break; case Z_SHUTDOWN: case Z_REBOOT: case Z_NOTE_UNINSTALLING: case Z_MOUNT: + case Z_FORCEMOUNT: case Z_UNMOUNT: if (kernelcall) /* Invalid; can't happen */ abort(); @@ -1518,7 +1759,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case Z_UNMOUNT: if (kernelcall) /* Invalid; can't happen */ abort(); - rval = zone_halt(zlogp, B_TRUE, B_FALSE, zstate); + rval = zone_halt(zlogp, B_TRUE, B_FALSE, zstate, debug); if (rval == 0) { eventstream_write(Z_EVT_ZONE_HALTED); (void) sema_post(&scratch_sem); @@ -1540,15 +1781,18 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case ZONE_STATE_DOWN: switch (cmd) { case Z_READY: - if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate)) - != 0) + if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate, + debug)) != 0) break; - if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) == 0) + zcons_statechanged(); + if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, + debug)) == 0) eventstream_write(Z_EVT_ZONE_READIED); else eventstream_write(Z_EVT_ZONE_HALTED); break; case Z_BOOT: + case Z_FORCEBOOT: /* * We could have two clients racing to boot this * zone; the second client loses, but his request @@ -1559,32 +1803,40 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = 0; break; case Z_HALT: - if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate)) - != 0) + if (kernelcall) { + log_init_exit(init_status); + } else { + log_init_exit(-1); + } + if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate, + debug)) != 0) break; eventstream_write(Z_EVT_ZONE_HALTED); + zcons_statechanged(); break; case Z_REBOOT: (void) strlcpy(boot_args, zargp->bootbuf, sizeof (boot_args)); eventstream_write(Z_EVT_ZONE_REBOOTING); - if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate)) - != 0) { + if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate, + debug)) != 0) { eventstream_write(Z_EVT_ZONE_BOOTFAILED); boot_args[0] = '\0'; break; } - if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) - != 0) { + zcons_statechanged(); + if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, + debug)) != 0) { eventstream_write(Z_EVT_ZONE_BOOTFAILED); boot_args[0] = '\0'; break; } - rval = zone_bootup(zlogp, zargp->bootbuf, zstate); + rval = zone_bootup(zlogp, zargp->bootbuf, zstate, + debug); audit_put_record(zlogp, uc, rval, "reboot"); if (rval != 0) { (void) zone_halt(zlogp, B_FALSE, B_TRUE, - zstate); + zstate, debug); eventstream_write(Z_EVT_ZONE_BOOTFAILED); } boot_args[0] = '\0'; @@ -1596,6 +1848,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, break; case Z_NOTE_UNINSTALLING: case Z_MOUNT: + case Z_FORCEMOUNT: case Z_UNMOUNT: zerror(zlogp, B_FALSE, "%s operation is invalid " "for zones in state '%s'", z_cmd_name(cmd), @@ -1759,11 +2012,38 @@ top: * state. */ if (zstate > ZONE_STATE_INSTALLED) { + static zoneid_t zid; + zerror(zlogp, B_FALSE, "zone '%s': WARNING: zone is in state '%s', but " "zoneadmd does not appear to be available; " "restarted zoneadmd to recover.", zone_name, zone_state_str(zstate)); + + /* + * Startup a thread to perform the zfd logging/tty svc + * and a thread to perform memory capping for the + * zone. zlogp won't be valid for much longer so use + * logsys. + */ + if ((zid = getzoneidbyname(zone_name)) != -1) { + create_log_thread(&logsys, zid); + create_mcap_thread(&logsys, zid); + } + + /* recover the global configuration snapshot */ + if (snap_hndl == NULL) { + if ((snap_hndl = zonecfg_init_handle()) + == NULL || + zonecfg_create_snapshot(zone_name) + != Z_OK || + zonecfg_get_snapshot_handle(zone_name, + snap_hndl) != Z_OK) { + zerror(zlogp, B_FALSE, "recovering " + "zone configuration handle"); + goto out; + } + } } (void) fdetach(zone_door_path); @@ -1777,21 +2057,62 @@ out: } /* - * Setup the brand's pre and post state change callbacks, as well as the - * query callback, if any of these exist. + * Run the query hook with the 'env' parameter. It should return a + * string of tab-delimited key-value pairs, each of which should be set + * in the environment. + * + * Because the env_vars string values become part of the environment, the + * string is static and we don't free it. + * + * This function is always called before zoneadmd forks and makes itself + * exclusive, so it is possible there could more than one instance of zoneadmd + * running in parallel at this point. Thus, we have no zonecfg snapshot and + * shouldn't take one yet (i.e. snap_hndl is NULL). Thats ok, since we don't + * need any zonecfg info to query for a brand-specific env value. */ static int -brand_callback_init(brand_handle_t bh, char *zone_name) +set_brand_env(zlog_t *zlogp) { - char zpath[MAXPATHLEN]; + int ret = 0; + static char *env_vars = NULL; + char buf[2 * MAXPATHLEN]; + + if (query_hook[0] == '\0' || env_vars != NULL) + return (0); + + if (snprintf(buf, sizeof (buf), "%s env", query_hook) > sizeof (buf)) + return (-1); - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) + if (do_subproc(zlogp, buf, &env_vars, B_FALSE) != 0) return (-1); + if (env_vars != NULL) { + char *sp; + + sp = strtok(env_vars, "\t"); + while (sp != NULL) { + if (putenv(sp) != 0) { + ret = -1; + break; + } + sp = strtok(NULL, "\t"); + } + } + + return (ret); +} + +/* + * Setup the brand's pre and post state change callbacks, as well as the + * query callback, if any of these exist. + */ +static int +brand_callback_init(brand_handle_t bh, char *zone_name) +{ (void) strlcpy(pre_statechg_hook, EXEC_PREFIX, sizeof (pre_statechg_hook)); - if (brand_get_prestatechange(bh, zone_name, zpath, + if (brand_get_prestatechange(bh, zone_name, zonepath, pre_statechg_hook + EXEC_LEN, sizeof (pre_statechg_hook) - EXEC_LEN) != 0) return (-1); @@ -1802,7 +2123,7 @@ brand_callback_init(brand_handle_t bh, char *zone_name) (void) strlcpy(post_statechg_hook, EXEC_PREFIX, sizeof (post_statechg_hook)); - if (brand_get_poststatechange(bh, zone_name, zpath, + if (brand_get_poststatechange(bh, zone_name, zonepath, post_statechg_hook + EXEC_LEN, sizeof (post_statechg_hook) - EXEC_LEN) != 0) return (-1); @@ -1813,7 +2134,7 @@ brand_callback_init(brand_handle_t bh, char *zone_name) (void) strlcpy(query_hook, EXEC_PREFIX, sizeof (query_hook)); - if (brand_get_query(bh, zone_name, zpath, query_hook + EXEC_LEN, + if (brand_get_query(bh, zone_name, zonepath, query_hook + EXEC_LEN, sizeof (query_hook) - EXEC_LEN) != 0) return (-1); @@ -1941,6 +2262,11 @@ main(int argc, char *argv[]) return (1); } + if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { + zerror(zlogp, B_FALSE, "unable to determine zone path"); + return (-1); + } + if (zonecfg_default_brand(default_brand, sizeof (default_brand)) != Z_OK) { zerror(zlogp, B_FALSE, "unable to determine default brand"); @@ -2012,6 +2338,11 @@ main(int argc, char *argv[]) } priv_freeset(privset); + if (set_brand_env(zlogp) != 0) { + zerror(zlogp, B_FALSE, "Unable to setup brand's environment"); + return (1); + } + if (mkzonedir(zlogp) != 0) return (1); @@ -2052,6 +2383,13 @@ main(int argc, char *argv[]) (void) sigaddset(&block_cld, SIGCHLD); (void) sigprocmask(SIG_BLOCK, &block_cld, NULL); + /* + * The parent only needs stderr after the fork, so close other fd's + * that we inherited from zoneadm so that the parent doesn't have those + * open while waiting. The child will close the rest after the fork. + */ + closefrom(3); + if ((ctfd = init_template()) == -1) { zerror(zlogp, B_TRUE, "failed to create contract"); return (1); diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h index d784a303b3..7e5dcea432 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.h +++ b/usr/src/cmd/zoneadmd/zoneadmd.h @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014, Joyent, Inc. All rights reserved. */ #ifndef _ZONEADMD_H @@ -90,17 +91,19 @@ extern mutex_t msglock; extern boolean_t in_death_throes; extern boolean_t bringup_failure_recovery; extern char *zone_name; +extern char zonepath[MAXNAMELEN]; +extern zone_dochandle_t snap_hndl; extern char pool_name[MAXNAMELEN]; extern char brand_name[MAXNAMELEN]; extern char default_brand[MAXNAMELEN]; extern char boot_args[BOOTARGS_MAX]; -extern char bad_boot_arg[BOOTARGS_MAX]; extern boolean_t zone_isnative; extern boolean_t zone_iscluster; extern dladm_handle_t dld_handle; extern void zerror(zlog_t *, boolean_t, const char *, ...); extern char *localize_msg(char *locale, const char *msg); +extern void nwifent_free_attrs(struct zone_nwiftab *); /* * Eventstream interfaces. @@ -112,8 +115,7 @@ typedef enum { Z_EVT_ZONE_HALTED, Z_EVT_ZONE_READIED, Z_EVT_ZONE_UNINSTALLING, - Z_EVT_ZONE_BOOTFAILED, - Z_EVT_ZONE_BADARGS + Z_EVT_ZONE_BOOTFAILED } zone_evt_t; extern int eventstream_init(); @@ -135,9 +137,9 @@ typedef enum { /* * Virtual platform interfaces. */ -extern zoneid_t vplat_create(zlog_t *, zone_mnt_t); +extern zoneid_t vplat_create(zlog_t *, zone_mnt_t, zoneid_t); extern int vplat_bringup(zlog_t *, zone_mnt_t, zoneid_t); -extern int vplat_teardown(zlog_t *, boolean_t, boolean_t); +extern int vplat_teardown(zlog_t *, boolean_t, boolean_t, boolean_t); extern int vplat_get_iptype(zlog_t *, zone_iptype_t *); /* @@ -154,6 +156,19 @@ extern void resolve_lofs(zlog_t *zlogp, char *path, size_t pathlen); */ extern int init_console(zlog_t *); extern void serve_console(zlog_t *); +extern void zcons_statechanged(); + +/* + * Memory capping thread creation. + */ +extern void create_mcap_thread(zlog_t *, zoneid_t); +extern void destroy_mcap_thread(); + +/* + * Zone FD log thread creation. + */ +extern void create_log_thread(zlog_t *, zoneid_t); +extern void destroy_log_thread(); /* * Contract handling. @@ -163,7 +178,7 @@ extern int init_template(void); /* * Routine to manage child processes. */ -extern int do_subproc(zlog_t *, char *, char **); +extern int do_subproc(zlog_t *, char *, char **, boolean_t); #ifdef __cplusplus } diff --git a/usr/src/cmd/zonecfg/Makefile b/usr/src/cmd/zonecfg/Makefile index ae8f5c11d1..94d725776b 100644 --- a/usr/src/cmd/zonecfg/Makefile +++ b/usr/src/cmd/zonecfg/Makefile @@ -27,6 +27,7 @@ PROG= zonecfg OBJS= zonecfg.o zonecfg_lex.o zonecfg_grammar.tab.o include ../Makefile.cmd +include ../Makefile.ctf # zonecfg has a name clash with main() and libl.so.1. However, zonecfg must # still export a number of "yy*" (libl) interfaces. Reduce all other symbols @@ -36,7 +37,7 @@ MAPOPTS = $(MAPFILES:%=-M%) LFLAGS = -t YFLAGS = -d -b zonecfg_grammar -LDLIBS += -lzonecfg -ll -lnsl -ltecla -lzfs -lbrand -ldladm -linetutil +LDLIBS += -lzonecfg -ll -lnsl -ltecla -lzfs -lbrand -ldladm -linetutil -luuid CPPFLAGS += -I. LDFLAGS += $(MAPOPTS) CLEANFILES += zonecfg_lex.c zonecfg_grammar.tab.c zonecfg_grammar.tab.h diff --git a/usr/src/cmd/zonecfg/zonecfg.c b/usr/src/cmd/zonecfg/zonecfg.c index 3dbec383bf..235019ef46 100644 --- a/usr/src/cmd/zonecfg/zonecfg.c +++ b/usr/src/cmd/zonecfg/zonecfg.c @@ -23,6 +23,7 @@ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2014 Gary Mills + * Copyright 2013, Joyent Inc. All rights reserved. */ /* @@ -79,6 +80,7 @@ #include <libinetutil.h> #include <pwd.h> #include <inet/ip.h> +#include <uuid/uuid.h> #include <libzonecfg.h> #include "zonecfg.h" @@ -126,7 +128,7 @@ extern int lex_lineno; #define SHELP_REMOVE "remove [-F] <resource-type> " \ "[ <property-name>=<property-value> ]*\n" \ "\t(global scope)\n" \ - "remove <property-name> <property-value>\n" \ + "remove [-F] <property-name> <property-value>\n" \ "\t(resource scope)" #define SHELP_REVERT "revert [-F]" #define SHELP_SELECT "select <resource-type> { <property-name>=" \ @@ -187,6 +189,8 @@ char *res_types[] = { "admin", "fs-allowed", ALIAS_MAXPROCS, + ALIAS_ZFSPRI, + "uuid", NULL }; @@ -234,6 +238,12 @@ char *prop_types[] = { "fs-allowed", ALIAS_MAXPROCS, "allowed-address", + ALIAS_ZFSPRI, + "mac-addr", + "vlan-id", + "global-nic", + "property", + "uuid", NULL }; @@ -299,6 +309,7 @@ static const char *clear_cmds[] = { "clear " ALIAS_MAXSEMIDS, "clear " ALIAS_SHARES, "clear " ALIAS_MAXPROCS, + "clear " ALIAS_ZFSPRI, NULL }; @@ -349,6 +360,8 @@ static const char *set_cmds[] = { "set hostid=", "set fs-allowed=", "set " ALIAS_MAXPROCS "=", + "set " ALIAS_ZFSPRI "=", + "set uuid=", NULL }; @@ -381,6 +394,7 @@ static const char *info_cmds[] = { "info admin", "info fs-allowed", "info max-processes", + "info uuid", NULL }; @@ -406,9 +420,20 @@ static const char *net_res_scope_cmds[] = { "exit", "help", "info", + "add property ", + "clear allowed-address", + "clear defrouter", + "clear global-nic", + "clear mac-addr", + "clear vlan-id", + "remove property ", "set address=", - "set physical=", + "set allowed-address=", "set defrouter=", + "set global-nic=", + "set mac-addr=", + "set physical=", + "set vlan-id=", NULL }; @@ -418,6 +443,7 @@ static const char *device_res_scope_cmds[] = { "exit", "help", "info", + "add property ", "set match=", NULL }; @@ -525,6 +551,7 @@ static zone_dochandle_t handle; /* used all over the place */ static char zone[ZONENAME_MAX]; static char revert_zone[ZONENAME_MAX]; +static char new_uuid[UUID_PRINTABLE_STRING_LENGTH]; /* global brand operations */ static brand_handle_t brand; @@ -579,7 +606,6 @@ static struct zone_rctltab old_rctltab, in_progress_rctltab; static struct zone_attrtab old_attrtab, in_progress_attrtab; static struct zone_dstab old_dstab, in_progress_dstab; static struct zone_psettab old_psettab, in_progress_psettab; -static struct zone_mcaptab old_mcaptab, in_progress_mcaptab; static struct zone_admintab old_admintab, in_progress_admintab; static GetLine *gl; /* The gl_get_line() resource object */ @@ -1078,11 +1104,20 @@ usage(boolean_t verbose, uint_t flags) (void) fprintf(fp, gettext("Valid commands:\n")); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), gettext("<IP-address>")); + (void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS), gettext("<IP-address>")); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_PHYSICAL), gettext("<interface>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_MAC), gettext("<mac-address>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_GNIC), gettext("<global zone NIC>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_VLANID), gettext("<vlan ID>")); (void) fprintf(fp, gettext("See ifconfig(1M) for " "details of the <interface> string.\n")); (void) fprintf(fp, gettext("%s %s is valid " @@ -1090,10 +1125,12 @@ usage(boolean_t verbose, uint_t flags) "must not be set.\n"), cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), pt_to_str(PT_IPTYPE), gettext("shared")); - (void) fprintf(fp, gettext("%s %s is valid " - "if the %s property is set to %s, otherwise it " - "must not be set.\n"), - cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS), + (void) fprintf(fp, gettext("%s (%s, %s, %s, %s) are " + "valid if the %s property is set to %s, otherwise " + "they must not be set.\n"), + cmd_to_str(CMD_SET), + pt_to_str(PT_ALLOWED_ADDRESS), pt_to_str(PT_MAC), + pt_to_str(PT_VLANID), pt_to_str(PT_GNIC), pt_to_str(PT_IPTYPE), gettext("exclusive")); (void) fprintf(fp, gettext("\t%s %s=%s\n%s %s " "is valid if the %s or %s property is set, " @@ -1109,6 +1146,9 @@ usage(boolean_t verbose, uint_t flags) "used to configure a device node.\n"), rt_to_str(resource_scope)); (void) fprintf(fp, gettext("Valid commands:\n")); + (void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_MATCH), gettext("<device-path>")); break; @@ -1240,10 +1280,12 @@ usage(boolean_t verbose, uint_t flags) if (flags & HELP_USAGE) { (void) fprintf(fp, "%s:\t%s %s\n", gettext("usage"), execname, cmd_to_str(CMD_HELP)); - (void) fprintf(fp, "\t%s -z <zone>\t\t\t(%s)\n", + (void) fprintf(fp, "\t%s {-z <zone>|-u <uuid>}\t\t\t(%s)\n", execname, gettext("interactive")); - (void) fprintf(fp, "\t%s -z <zone> <command>\n", execname); - (void) fprintf(fp, "\t%s -z <zone> -f <command-file>\n", + (void) fprintf(fp, "\t%s {-z <zone>|-u <uuid>} <command>\n", + execname); + (void) fprintf(fp, + "\t%s {-z <zone>|-u <uuid>} -f <command-file>\n", execname); } if (flags & HELP_SUBCMDS) { @@ -1332,15 +1374,22 @@ usage(boolean_t verbose, uint_t flags) pt_to_str(PT_MAXSEMIDS)); (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), pt_to_str(PT_SHARES)); + (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), + pt_to_str(PT_UUID)); + (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), + pt_to_str(PT_ZFSPRI)); (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s\n", rt_to_str(RT_FS), pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW), pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS)); - (void) fprintf(fp, "\t%s\t\t%s, %s, %s|%s\n", rt_to_str(RT_NET), + (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s, %s, %s %s\n", + rt_to_str(RT_NET), pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS), - pt_to_str(PT_PHYSICAL), pt_to_str(PT_DEFROUTER)); - (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE), - pt_to_str(PT_MATCH)); + pt_to_str(PT_GNIC), pt_to_str(PT_MAC), + pt_to_str(PT_PHYSICAL), pt_to_str(PT_NPROP), + pt_to_str(PT_VLANID), pt_to_str(PT_DEFROUTER)); + (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_DEVICE), + pt_to_str(PT_MATCH), pt_to_str(PT_NPROP)); (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL), pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR), @@ -1395,6 +1444,9 @@ initialize(boolean_t handle_expected) if (zonecfg_check_handle(handle) != Z_OK) { if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) { got_handle = B_TRUE; + + (void) zonecfg_fix_obsolete(handle); + if (zonecfg_get_brand(handle, brandname, sizeof (brandname)) != Z_OK) { zerr("Zone %s is inconsistent: missing " @@ -1662,6 +1714,7 @@ create_func(cmd_t *cmd) boolean_t force = B_FALSE; boolean_t attach = B_FALSE; boolean_t arg_err = B_FALSE; + uuid_t uuid; assert(cmd != NULL); @@ -1669,7 +1722,7 @@ create_func(cmd_t *cmd) (void) strlcpy(zone_template, "SUNWdefault", sizeof (zone_template)); optind = 0; - while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:")) + while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:X")) != EOF) { switch (arg) { case '?': @@ -1695,6 +1748,17 @@ create_func(cmd_t *cmd) (void) strlcpy(zone_template, optarg, sizeof (zone_template)); break; + case 'X': + (void) snprintf(zone_template, sizeof (zone_template), + "%s/%s.xml", ZONE_CONFIG_ROOT, zone); + err = zonecfg_get_xml_handle(zone_template, handle); + if (err != Z_OK) { + zone_perror(execname, err, B_TRUE); + exit(Z_ERR); + } + got_handle = B_TRUE; + need_to_commit = B_TRUE; + return; default: short_usage(CMD_CREATE); arg_err = B_TRUE; @@ -1751,6 +1815,10 @@ create_func(cmd_t *cmd) zonecfg_fini_handle(handle); handle = tmphandle; got_handle = B_TRUE; + + /* Allocate a new uuid for this new zone */ + uuid_generate(uuid); + uuid_unparse(uuid, new_uuid); } /* @@ -1797,8 +1865,8 @@ export_func(cmd_t *cmd) struct zone_rctltab rctltab; struct zone_dstab dstab; struct zone_psettab psettab; - struct zone_mcaptab mcaptab; struct zone_rctlvaltab *valptr; + struct zone_res_attrtab *rap; struct zone_admintab admintab; int err, arg; char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN]; @@ -1811,6 +1879,7 @@ export_func(cmd_t *cmd) FILE *of; boolean_t autoboot; zone_iptype_t iptype; + uuid_t uuid; boolean_t need_to_close = B_FALSE; boolean_t arg_err = B_FALSE; @@ -1921,6 +1990,14 @@ export_func(cmd_t *cmd) pt_to_str(PT_FS_ALLOWED), fsallowedp); } + if (zonecfg_get_uuid(zone, uuid) == Z_OK && !uuid_is_null(uuid)) { + char suuid[UUID_PRINTABLE_STRING_LENGTH]; + + uuid_unparse(uuid, suuid); + (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_UUID), suuid); + } + if ((err = zonecfg_setfsent(handle)) != Z_OK) { zone_perror(zone, err, B_FALSE); goto done; @@ -1968,7 +2045,17 @@ export_func(cmd_t *cmd) export_prop(of, PT_ALLOWED_ADDRESS, nwiftab.zone_nwif_allowed_address); export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical); + export_prop(of, PT_MAC, nwiftab.zone_nwif_mac); + export_prop(of, PT_VLANID, nwiftab.zone_nwif_vlan_id); + export_prop(of, PT_GNIC, nwiftab.zone_nwif_gnic); export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter); + for (rap = nwiftab.zone_nwif_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), rap->zone_res_attr_name, + pt_to_str(PT_VALUE), rap->zone_res_attr_value); + } (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); } (void) zonecfg_endnwifent(handle); @@ -1981,21 +2068,17 @@ export_func(cmd_t *cmd) (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), rt_to_str(RT_DEVICE)); export_prop(of, PT_MATCH, devtab.zone_dev_match); + for (rap = devtab.zone_dev_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), rap->zone_res_attr_name, + pt_to_str(PT_VALUE), rap->zone_res_attr_value); + } (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); } (void) zonecfg_enddevent(handle); - if (zonecfg_getmcapent(handle, &mcaptab) == Z_OK) { - char buf[128]; - - (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), - rt_to_str(RT_MCAP)); - bytes_to_units(mcaptab.zone_physmem_cap, buf, sizeof (buf)); - (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), - pt_to_str(PT_PHYSICAL), buf); - (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); - } - if ((err = zonecfg_setrctlent(handle)) != Z_OK) { zone_perror(zone, err, B_FALSE); goto done; @@ -2149,7 +2232,6 @@ add_resource(cmd_t *cmd) { int type; struct zone_psettab tmp_psettab; - struct zone_mcaptab tmp_mcaptab; uint64_t tmp; uint64_t tmp_mcap; char pool[MAXNAMELEN]; @@ -2241,9 +2323,10 @@ add_resource(cmd_t *cmd) * Make sure there isn't already a mem-cap entry or max-swap * or max-locked rctl. */ - if (zonecfg_lookup_mcap(handle, &tmp_mcaptab) == Z_OK || - zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp_mcap) - == Z_OK || + if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, + &tmp_mcap) == Z_OK || + zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, + &tmp_mcap) == Z_OK || zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp_mcap) == Z_OK) { zerr(gettext("The %s resource or a related resource " @@ -2256,7 +2339,6 @@ add_resource(cmd_t *cmd) "to even the root user; " "this could render the system impossible\n" "to administer. Please use caution.")); - bzero(&in_progress_mcaptab, sizeof (in_progress_mcaptab)); return; case RT_ADMIN: bzero(&in_progress_admintab, sizeof (in_progress_admintab)); @@ -2359,6 +2441,68 @@ bad: zonecfg_free_rctl_value_list(rctlvaltab); } +/* + * Resource attribute ("property" resource embedded on net or dev resource) + */ +static void +do_res_attr(struct zone_res_attrtab **headp, complex_property_ptr_t cpp) +{ + complex_property_ptr_t cp; + struct zone_res_attrtab *np; + int err; + boolean_t seen_name = B_FALSE, seen_value = B_FALSE; + + if ((np = calloc(1, sizeof (struct zone_res_attrtab))) == NULL) { + zone_perror(zone, Z_NOMEM, B_TRUE); + exit(Z_ERR); + } + + for (cp = cpp; cp != NULL; cp = cp->cp_next) { + switch (cp->cp_type) { + case PT_NAME: + if (seen_name) { + zerr(gettext("%s already specified"), + pt_to_str(PT_NAME)); + goto bad; + } + (void) strlcpy(np->zone_res_attr_name, cp->cp_value, + sizeof (np->zone_res_attr_name)); + seen_name = B_TRUE; + break; + case PT_VALUE: + if (seen_value) { + zerr(gettext("%s already specified"), + pt_to_str(PT_VALUE)); + goto bad; + } + (void) strlcpy(np->zone_res_attr_value, cp->cp_value, + sizeof (np->zone_res_attr_value)); + seen_value = B_TRUE; + break; + default: + zone_perror(pt_to_str(PT_NPROP), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_ADD, B_TRUE); + usage(B_FALSE, HELP_PROPS); + zonecfg_free_res_attr_list(np); + return; + } + } + + if (!seen_name) + zerr(gettext("%s not specified"), pt_to_str(PT_NAME)); + if (!seen_value) + zerr(gettext("%s not specified"), pt_to_str(PT_VALUE)); + + err = zonecfg_add_res_attr(headp, np); + if (err != Z_OK) + zone_perror(pt_to_str(PT_NPROP), err, B_TRUE); + return; + +bad: + zonecfg_free_res_attr_list(np); +} + static void add_property(cmd_t *cmd) { @@ -2426,6 +2570,44 @@ add_property(cmd_t *cmd) } } return; + case RT_NET: + if (prop_type != PT_NPROP) { + zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_ADD, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + pp = cmd->cmd_property_ptr[0]; + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + + do_res_attr(&(in_progress_nwiftab.zone_nwif_attrp), + pp->pv_complex); + return; + case RT_DEVICE: + if (prop_type != PT_NPROP) { + zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_ADD, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + pp = cmd->cmd_property_ptr[0]; + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + + do_res_attr(&(in_progress_devtab.zone_dev_attrp), + pp->pv_complex); + return; case RT_RCTL: if (prop_type != PT_VALUE) { zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, @@ -2470,7 +2652,7 @@ static boolean_t gz_invalid_rt_property(int type) { return (global_zone && (type == RT_ZONENAME || type == RT_ZONEPATH || - type == RT_AUTOBOOT || type == RT_LIMITPRIV || + type == RT_AUTOBOOT || type == RT_LIMITPRIV || type == RT_UUID || type == RT_BOOTARGS || type == RT_BRAND || type == RT_SCHED || type == RT_IPTYPE || type == RT_HOSTID || type == RT_FS_ALLOWED)); } @@ -2479,7 +2661,7 @@ static boolean_t gz_invalid_property(int type) { return (global_zone && (type == PT_ZONENAME || type == PT_ZONEPATH || - type == PT_AUTOBOOT || type == PT_LIMITPRIV || + type == PT_AUTOBOOT || type == PT_LIMITPRIV || type == PT_UUID || type == PT_BOOTARGS || type == PT_BRAND || type == PT_SCHED || type == PT_IPTYPE || type == PT_HOSTID || type == PT_FS_ALLOWED)); } @@ -2530,8 +2712,9 @@ add_func(cmd_t *cmd) resource_scope = cmd->cmd_res_type; end_op = CMD_ADD; add_resource(cmd); - } else + } else { add_property(cmd); + } } /* @@ -2696,6 +2879,32 @@ fill_in_fstab(cmd_t *cmd, struct zone_fstab *fstab, boolean_t fill_in_only) return (zonecfg_lookup_filesystem(handle, fstab)); } +/* + * Turn an addr that looks like f:2:0:44:5:6C into 0f:02:00:44:05:6c + * We're expecting a dst of at least MAXMACADDRLEN size here. + */ +static void +normalize_mac_addr(char *dst, const char *src, int len) +{ + char *p, *e, *sep = ""; + long n; + char buf[MAXMACADDRLEN], tmp[4]; + + *dst = '\0'; + (void) strlcpy(buf, src, sizeof (buf)); + p = strtok(buf, ":"); + while (p != NULL) { + n = strtol(p, &e, 16); + if (*e != NULL || n > 0xff) + return; + (void) snprintf(tmp, sizeof (tmp), "%s%02x", sep, n); + (void) strlcat(dst, tmp, len); + + sep = ":"; + p = strtok(NULL, ":"); + } +} + static int fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, boolean_t fill_in_only) @@ -2729,6 +2938,21 @@ fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, pp->pv_simple, sizeof (nwiftab->zone_nwif_physical)); break; + case PT_MAC: + normalize_mac_addr(nwiftab->zone_nwif_mac, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_mac)); + break; + case PT_VLANID: + (void) strlcpy(nwiftab->zone_nwif_vlan_id, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_vlan_id)); + break; + case PT_GNIC: + (void) strlcpy(nwiftab->zone_nwif_gnic, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_gnic)); + break; case PT_DEFROUTER: (void) strlcpy(nwiftab->zone_nwif_defrouter, pp->pv_simple, @@ -2979,6 +3203,8 @@ prompt_remove_resource(cmd_t *cmd, char *rsrc) num = zonecfg_num_resources(handle, rsrc); if (num == 0) { + if (force) + return (B_TRUE); z_cmd_rt_perror(CMD_REMOVE, cmd->cmd_res_type, Z_NO_ENTRY, B_TRUE); return (B_FALSE); @@ -3007,7 +3233,7 @@ prompt_remove_resource(cmd_t *cmd, char *rsrc) } static void -remove_fs(cmd_t *cmd) +remove_fs(cmd_t *cmd, boolean_t force) { int err; @@ -3016,13 +3242,16 @@ remove_fs(cmd_t *cmd) struct zone_fstab fstab; if ((err = fill_in_fstab(cmd, &fstab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); return; } - if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); - else + if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); + } else { need_to_commit = B_TRUE; + } zonecfg_free_fs_option_list(fstab.zone_fs_options); return; } @@ -3041,7 +3270,7 @@ remove_fs(cmd_t *cmd) } static void -remove_net(cmd_t *cmd) +remove_net(cmd_t *cmd, boolean_t force) { int err; @@ -3050,13 +3279,18 @@ remove_net(cmd_t *cmd) struct zone_nwiftab nwiftab; if ((err = fill_in_nwiftab(cmd, &nwiftab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, + B_TRUE); return; } - if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, B_TRUE); - else + if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3074,7 +3308,7 @@ remove_net(cmd_t *cmd) } static void -remove_device(cmd_t *cmd) +remove_device(cmd_t *cmd, boolean_t force) { int err; @@ -3083,13 +3317,18 @@ remove_device(cmd_t *cmd) struct zone_devtab devtab; if ((err = fill_in_devtab(cmd, &devtab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, + B_TRUE); return; } - if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, B_TRUE); - else + if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3107,7 +3346,7 @@ remove_device(cmd_t *cmd) } static void -remove_attr(cmd_t *cmd) +remove_attr(cmd_t *cmd, boolean_t force) { int err; @@ -3116,13 +3355,18 @@ remove_attr(cmd_t *cmd) struct zone_attrtab attrtab; if ((err = fill_in_attrtab(cmd, &attrtab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, + B_TRUE); return; } - if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, B_TRUE); - else + if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3140,7 +3384,7 @@ remove_attr(cmd_t *cmd) } static void -remove_dataset(cmd_t *cmd) +remove_dataset(cmd_t *cmd, boolean_t force) { int err; @@ -3149,13 +3393,18 @@ remove_dataset(cmd_t *cmd) struct zone_dstab dstab; if ((err = fill_in_dstab(cmd, &dstab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, + B_TRUE); return; } - if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, B_TRUE); - else + if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3173,7 +3422,7 @@ remove_dataset(cmd_t *cmd) } static void -remove_rctl(cmd_t *cmd) +remove_rctl(cmd_t *cmd, boolean_t force) { int err; @@ -3182,13 +3431,18 @@ remove_rctl(cmd_t *cmd) struct zone_rctltab rctltab; if ((err = fill_in_rctltab(cmd, &rctltab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, + B_TRUE); return; } - if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, B_TRUE); - else + if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); return; } @@ -3207,72 +3461,90 @@ remove_rctl(cmd_t *cmd) } static void -remove_pset() +remove_pset(boolean_t force) { int err; struct zone_psettab psettab; if ((err = zonecfg_lookup_pset(handle, &psettab)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); return; } - if ((err = zonecfg_delete_pset(handle)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); - else + if ((err = zonecfg_delete_pset(handle)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); + } else { need_to_commit = B_TRUE; + } } static void -remove_pcap() +remove_pcap(boolean_t force) { int err; uint64_t tmp; if (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &tmp) != Z_OK) { - zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), rt_to_str(RT_PCAP), - zonecfg_strerror(Z_NO_RESOURCE_TYPE)); - saw_error = B_TRUE; + if (!force) { + zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), + rt_to_str(RT_PCAP), + zonecfg_strerror(Z_NO_RESOURCE_TYPE)); + saw_error = B_TRUE; + } return; } - if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_CPUCAP)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_PCAP, err, B_TRUE); - else + if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_CPUCAP)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_PCAP, err, B_TRUE); + } else { need_to_commit = B_TRUE; + } } static void -remove_mcap() +remove_mcap(boolean_t force) { int err, res1, res2, res3; uint64_t tmp; - struct zone_mcaptab mcaptab; boolean_t revert = B_FALSE; - res1 = zonecfg_lookup_mcap(handle, &mcaptab); + res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &tmp); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp); res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp); /* if none of these exist, there is no resource to remove */ if (res1 != Z_OK && res2 != Z_OK && res3 != Z_OK) { - zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), rt_to_str(RT_MCAP), - zonecfg_strerror(Z_NO_RESOURCE_TYPE)); - saw_error = B_TRUE; + if (!force) { + zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), + rt_to_str(RT_MCAP), + zonecfg_strerror(Z_NO_RESOURCE_TYPE)); + saw_error = B_TRUE; + } return; } if (res1 == Z_OK) { - if ((err = zonecfg_delete_mcap(handle)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE); - revert = B_TRUE; + if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXPHYSMEM)) + != Z_OK) { + if (!force) { + z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, + B_TRUE); + revert = B_TRUE; + } } else { need_to_commit = B_TRUE; } } + if (res2 == Z_OK) { if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXSWAP)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE); - revert = B_TRUE; + if (!force) { + z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, + B_TRUE); + revert = B_TRUE; + } } else { need_to_commit = B_TRUE; } @@ -3280,8 +3552,11 @@ remove_mcap() if (res3 == Z_OK) { if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE); - revert = B_TRUE; + if (!force) { + z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, + B_TRUE); + revert = B_TRUE; + } } else { need_to_commit = B_TRUE; } @@ -3292,7 +3567,7 @@ remove_mcap() } static void -remove_admin(cmd_t *cmd) +remove_admin(cmd_t *cmd, boolean_t force) { int err; @@ -3301,34 +3576,33 @@ remove_admin(cmd_t *cmd) struct zone_admintab admintab; if ((err = fill_in_admintab(cmd, &admintab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, - err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, err, + B_TRUE); return; } if ((err = zonecfg_delete_admin(handle, &admintab, - zone)) - != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, - err, B_TRUE); - else + zone)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; - } else { - /* - * unqualified admin removal. - * remove all admins but prompt if more - * than one. - */ - if (!prompt_remove_resource(cmd, "admin")) - return; - - if ((err = zonecfg_delete_admins(handle, zone)) - != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, - err, B_TRUE); - else - need_to_commit = B_TRUE; } + + /* + * unqualified admin removal. + * remove all admins but prompt if more than one. + */ + if (!prompt_remove_resource(cmd, "admin")) + return; + + if ((err = zonecfg_delete_admins(handle, zone)) != Z_OK) + z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, err, B_TRUE); + else + need_to_commit = B_TRUE; } static void @@ -3337,6 +3611,7 @@ remove_resource(cmd_t *cmd) int type; int arg; boolean_t arg_err = B_FALSE; + boolean_t force = B_FALSE; if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { long_usage(CMD_REMOVE, B_TRUE); @@ -3351,6 +3626,7 @@ remove_resource(cmd_t *cmd) arg_err = B_TRUE; break; case 'F': + force = B_TRUE; break; default: short_usage(CMD_REMOVE); @@ -3366,34 +3642,34 @@ remove_resource(cmd_t *cmd) switch (type) { case RT_FS: - remove_fs(cmd); + remove_fs(cmd, force); return; case RT_NET: - remove_net(cmd); + remove_net(cmd, force); return; case RT_DEVICE: - remove_device(cmd); + remove_device(cmd, force); return; case RT_RCTL: - remove_rctl(cmd); + remove_rctl(cmd, force); return; case RT_ATTR: - remove_attr(cmd); + remove_attr(cmd, force); return; case RT_DATASET: - remove_dataset(cmd); + remove_dataset(cmd, force); return; case RT_DCPU: - remove_pset(); + remove_pset(force); return; case RT_PCAP: - remove_pcap(); + remove_pcap(force); return; case RT_MCAP: - remove_mcap(); + remove_mcap(force); return; case RT_ADMIN: - remove_admin(cmd); + remove_admin(cmd, force); return; default: zone_perror(rt_to_str(type), Z_NO_RESOURCE_TYPE, B_TRUE); @@ -3410,7 +3686,27 @@ remove_property(cmd_t *cmd) int err, res_type, prop_type; property_value_ptr_t pp; struct zone_rctlvaltab *rctlvaltab; + struct zone_res_attrtab *np; complex_property_ptr_t cx; + int arg; + boolean_t force = B_FALSE; + boolean_t arg_err = B_FALSE; + + optind = 0; + while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "F")) != EOF) { + switch (arg) { + case 'F': + force = B_TRUE; + break; + default: + arg_err = B_TRUE; + break; + } + } + if (arg_err) { + saw_error = B_TRUE; + return; + } res_type = resource_scope; prop_type = cmd->cmd_prop_name[0]; @@ -3452,7 +3748,7 @@ remove_property(cmd_t *cmd) prop_id = pp->pv_simple; err = zonecfg_remove_fs_option(&in_progress_fstab, prop_id); - if (err != Z_OK) + if (err != Z_OK && !force) zone_perror(pt_to_str(prop_type), err, B_TRUE); } else { list_property_ptr_t list; @@ -3464,12 +3760,62 @@ remove_property(cmd_t *cmd) break; err = zonecfg_remove_fs_option( &in_progress_fstab, prop_id); - if (err != Z_OK) + if (err != Z_OK && !force) zone_perror(pt_to_str(prop_type), err, B_TRUE); } } return; + case RT_NET: /* FALLTHRU */ + case RT_DEVICE: + if (prop_type != PT_NPROP) { + zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_REMOVE, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + pp = cmd->cmd_property_ptr[0]; + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + + np = alloca(sizeof (struct zone_res_attrtab)); + for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) { + switch (cx->cp_type) { + case PT_NAME: + (void) strlcpy(np->zone_res_attr_name, + cx->cp_value, + sizeof (np->zone_res_attr_name)); + break; + case PT_VALUE: + (void) strlcpy(np->zone_res_attr_value, + cx->cp_value, + sizeof (np->zone_res_attr_value)); + break; + default: + zone_perror(pt_to_str(prop_type), + Z_NO_PROPERTY_TYPE, B_TRUE); + long_usage(CMD_REMOVE, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + } + np->zone_res_attr_next = NULL; + + if (res_type == RT_NET) { + err = zonecfg_remove_res_attr( + &(in_progress_nwiftab.zone_nwif_attrp), np); + } else { /* RT_DEVICE */ + err = zonecfg_remove_res_attr( + &(in_progress_devtab.zone_dev_attrp), np); + } + if (err != Z_OK && !force) + zone_perror(pt_to_str(prop_type), err, B_TRUE); + return; case RT_RCTL: if (prop_type != PT_VALUE) { zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, @@ -3518,22 +3864,10 @@ remove_property(cmd_t *cmd) rctlvaltab->zone_rctlval_next = NULL; err = zonecfg_remove_rctl_value(&in_progress_rctltab, rctlvaltab); - if (err != Z_OK) + if (err != Z_OK && !force) zone_perror(pt_to_str(prop_type), err, B_TRUE); zonecfg_free_rctl_value_list(rctlvaltab); return; - case RT_NET: - if (prop_type != PT_DEFROUTER) { - zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, - B_TRUE); - long_usage(CMD_REMOVE, B_TRUE); - usage(B_FALSE, HELP_PROPS); - return; - } else { - bzero(&in_progress_nwiftab.zone_nwif_defrouter, - sizeof (in_progress_nwiftab.zone_nwif_defrouter)); - return; - } default: zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, B_TRUE); long_usage(CMD_REMOVE, B_TRUE); @@ -3596,8 +3930,7 @@ clear_property(cmd_t *cmd) case RT_MCAP: switch (prop_type) { case PT_PHYSICAL: - in_progress_mcaptab.zone_physmem_cap[0] = '\0'; - need_to_commit = B_TRUE; + remove_aliased_rctl(PT_PHYSICAL, ALIAS_MAXPHYSMEM); return; case PT_SWAP: remove_aliased_rctl(PT_SWAP, ALIAS_MAXSWAP); @@ -3607,6 +3940,30 @@ clear_property(cmd_t *cmd) return; } break; + case RT_NET: + switch (prop_type) { + case PT_ALLOWED_ADDRESS: + in_progress_nwiftab.zone_nwif_allowed_address[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_DEFROUTER: + in_progress_nwiftab.zone_nwif_defrouter[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_GNIC: + in_progress_nwiftab.zone_nwif_gnic[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_MAC: + in_progress_nwiftab.zone_nwif_mac[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_VLANID: + in_progress_nwiftab.zone_nwif_vlan_id[0] = '\0'; + need_to_commit = B_TRUE; + return; + } + break; default: break; } @@ -3632,6 +3989,8 @@ clear_global(cmd_t *cmd) /* FALLTHRU */ case PT_ZONEPATH: /* FALLTHRU */ + case PT_UUID: + /* FALLTHRU */ case PT_BRAND: zone_perror(pt_to_str(type), Z_CLEAR_DISALLOW, B_TRUE); return; @@ -3694,6 +4053,9 @@ clear_global(cmd_t *cmd) case PT_SHARES: remove_aliased_rctl(PT_SHARES, ALIAS_SHARES); return; + case PT_ZFSPRI: + remove_aliased_rctl(PT_ZFSPRI, ALIAS_ZFSPRI); + return; case PT_HOSTID: if ((err = zonecfg_set_hostid(handle, NULL)) != Z_OK) z_cmd_rt_perror(CMD_CLEAR, RT_HOSTID, err, B_TRUE); @@ -3739,7 +4101,7 @@ clear_func(cmd_t *cmd) void select_func(cmd_t *cmd) { - int type, err, res; + int type, err; uint64_t limit; uint64_t tmp; @@ -3834,7 +4196,8 @@ select_func(cmd_t *cmd) return; case RT_MCAP: /* if none of these exist, there is no resource to select */ - if ((res = zonecfg_lookup_mcap(handle, &old_mcaptab)) != Z_OK && + if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &limit) + != Z_OK && zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &limit) != Z_OK && zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &limit) @@ -3843,12 +4206,6 @@ select_func(cmd_t *cmd) B_TRUE); global_scope = B_TRUE; } - if (res == Z_OK) - bcopy(&old_mcaptab, &in_progress_mcaptab, - sizeof (struct zone_mcaptab)); - else - bzero(&in_progress_mcaptab, - sizeof (in_progress_mcaptab)); return; case RT_ADMIN: if ((err = fill_in_admintab(cmd, &old_admintab, B_FALSE)) @@ -4115,7 +4472,6 @@ set_func(cmd_t *cmd) boolean_t autoboot; zone_iptype_t iptype; boolean_t force_set = B_FALSE; - size_t physmem_size = sizeof (in_progress_mcaptab.zone_physmem_cap); uint64_t mem_cap, mem_limit; float cap; char *unitp; @@ -4190,6 +4546,10 @@ set_func(cmd_t *cmd) res_type = RT_HOSTID; } else if (prop_type == PT_FS_ALLOWED) { res_type = RT_FS_ALLOWED; + } else if (prop_type == PT_ZFSPRI) { + res_type = RT_ZFSPRI; + } else if (prop_type == PT_UUID) { + res_type = RT_UUID; } else { zerr(gettext("Cannot set a resource-specific property " "from the global scope.")); @@ -4219,10 +4579,12 @@ set_func(cmd_t *cmd) * A nasty expression but not that complicated: * 1. fs options are simple or list (tested below) * 2. rctl value's are complex or list (tested below) + * 3. net attr's are complex (tested below) * Anything else should be simple. */ if (!(res_type == RT_FS && prop_type == PT_OPTIONS) && !(res_type == RT_RCTL && prop_type == PT_VALUE) && + !(res_type == RT_NET && prop_type == PT_NPROP) && (pp->pv_type != PROP_VAL_SIMPLE || (prop_id = pp->pv_simple) == NULL)) { zerr(gettext("A %s value was expected here."), @@ -4395,6 +4757,9 @@ set_func(cmd_t *cmd) case RT_SHARES: set_aliased_rctl(ALIAS_SHARES, prop_type, prop_id); return; + case RT_ZFSPRI: + set_aliased_rctl(ALIAS_ZFSPRI, prop_type, prop_id); + return; case RT_HOSTID: if ((err = zonecfg_set_hostid(handle, prop_id)) != Z_OK) { if (err == Z_TOO_BIG) { @@ -4408,6 +4773,15 @@ set_func(cmd_t *cmd) } need_to_commit = B_TRUE; return; + case RT_UUID: + /* + * We can't set here. We have to wait until commit since the + * uuid will be updating the index file and we may not have + * created the zone yet. + */ + (void) strlcpy(new_uuid, prop_id, sizeof (new_uuid)); + need_to_commit = B_TRUE; + return; case RT_FS_ALLOWED: if ((err = zonecfg_set_fs_allowed(handle, prop_id)) != Z_OK) zone_perror(zone, err, B_TRUE); @@ -4482,6 +4856,21 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_nwiftab.zone_nwif_physical)); break; + case PT_MAC: + normalize_mac_addr(in_progress_nwiftab.zone_nwif_mac, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_mac)); + break; + case PT_VLANID: + (void) strlcpy(in_progress_nwiftab.zone_nwif_vlan_id, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_vlan_id)); + break; + case PT_GNIC: + (void) strlcpy(in_progress_nwiftab.zone_nwif_gnic, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_gnic)); + break; case PT_DEFROUTER: if (validate_net_address_syntax(prop_id, B_TRUE) != Z_OK) { @@ -4492,6 +4881,20 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_nwiftab.zone_nwif_defrouter)); break; + case PT_NPROP: + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + zonecfg_free_res_attr_list( + in_progress_nwiftab.zone_nwif_attrp); + in_progress_nwiftab.zone_nwif_attrp = NULL; + if (!(pp->pv_type == PROP_VAL_LIST && + pp->pv_list == NULL)) + add_property(cmd); + break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, B_TRUE); @@ -4507,6 +4910,20 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_devtab.zone_dev_match)); break; + case PT_NPROP: + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + zonecfg_free_res_attr_list( + in_progress_devtab.zone_dev_attrp); + in_progress_devtab.zone_dev_attrp = NULL; + if (!(pp->pv_type == PROP_VAL_LIST && + pp->pv_list == NULL)) + add_property(cmd); + break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, B_TRUE); @@ -4667,18 +5084,30 @@ set_func(cmd_t *cmd) case RT_MCAP: switch (prop_type) { case PT_PHYSICAL: + /* + * We have to check if an rctl is allowed here since + * there might already be a rctl defined that blocks + * the alias. + */ + if (!zonecfg_aliased_rctl_ok(handle, + ALIAS_MAXPHYSMEM)) { + zone_perror(pt_to_str(PT_LOCKED), + Z_ALIAS_DISALLOW, B_FALSE); + saw_error = B_TRUE; + return; + } + if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) { - zerr(gettext("A positive number with a " + zerr(gettext("A non-negative number with a " "required scale suffix (K, M, G or T) was " - "expected here.")); - saw_error = B_TRUE; - } else if (mem_cap < ONE_MB) { - zerr(gettext("%s value is too small. It must " - "be at least 1M."), pt_to_str(PT_PHYSICAL)); + "expected\nhere.")); saw_error = B_TRUE; } else { - snprintf(in_progress_mcaptab.zone_physmem_cap, - physmem_size, "%llu", mem_cap); + if ((err = zonecfg_set_aliased_rctl(handle, + ALIAS_MAXPHYSMEM, mem_cap)) != Z_OK) + zone_perror(zone, err, B_TRUE); + else + need_to_commit = B_TRUE; } break; case PT_SWAP: @@ -4950,6 +5379,23 @@ info_hostid(zone_dochandle_t handle, FILE *fp) } static void +info_uuid(FILE *fp) +{ + uuid_t uuid; + char suuid[UUID_PRINTABLE_STRING_LENGTH]; + + if (new_uuid[0] != '\0') { + (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_UUID), new_uuid); + } else if (zonecfg_get_uuid(zone, uuid) == Z_OK && + !uuid_is_null(uuid)) { + uuid_unparse(uuid, suuid); + (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_UUID), suuid); + } else { + (void) fprintf(fp, "%s:\n", pt_to_str(PT_UUID)); + } +} + +static void info_fs_allowed(zone_dochandle_t handle, FILE *fp) { char fsallowedp[ZONE_FS_ALLOWED_MAX]; @@ -5031,12 +5477,25 @@ loopend: static void output_net(FILE *fp, struct zone_nwiftab *nwiftab) { + struct zone_res_attrtab *np; + (void) fprintf(fp, "%s:\n", rt_to_str(RT_NET)); output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE); output_prop(fp, PT_ALLOWED_ADDRESS, nwiftab->zone_nwif_allowed_address, B_TRUE); - output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE); output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE); + output_prop(fp, PT_GNIC, nwiftab->zone_nwif_gnic, B_TRUE); + output_prop(fp, PT_MAC, nwiftab->zone_nwif_mac, B_TRUE); + output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE); + output_prop(fp, PT_VLANID, nwiftab->zone_nwif_vlan_id, B_TRUE); + + for (np = nwiftab->zone_nwif_attrp; np != NULL; + np = np->zone_res_attr_next) { + fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n", + pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), np->zone_res_attr_name, + pt_to_str(PT_VALUE), np->zone_res_attr_value); + } } static void @@ -5079,8 +5538,18 @@ info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) static void output_dev(FILE *fp, struct zone_devtab *devtab) { + struct zone_res_attrtab *np; + (void) fprintf(fp, "%s:\n", rt_to_str(RT_DEVICE)); output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE); + + for (np = devtab->zone_dev_attrp; np != NULL; + np = np->zone_res_attr_next) { + fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n", + pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), np->zone_res_attr_name, + pt_to_str(PT_VALUE), np->zone_res_attr_value); + } } static void @@ -5339,15 +5808,18 @@ bytes_to_units(char *str, char *buf, int bufsize) } static void -output_mcap(FILE *fp, struct zone_mcaptab *mcaptab, int showswap, +output_mcap(FILE *fp, int showphys, uint64_t maxphys, int showswap, uint64_t maxswap, int showlocked, uint64_t maxlocked) { char buf[128]; (void) fprintf(fp, "%s:\n", rt_to_str(RT_MCAP)); - if (mcaptab->zone_physmem_cap[0] != '\0') { - bytes_to_units(mcaptab->zone_physmem_cap, buf, sizeof (buf)); - output_prop(fp, PT_PHYSICAL, buf, B_TRUE); + + if (showphys == Z_OK) { + (void) snprintf(buf, sizeof (buf), "%llu", maxphys); + bytes_to_units(buf, buf, sizeof (buf)); + /* Print directly since "physical" also is a net property. */ + (void) fprintf(fp, "\t[%s: %s]\n", pt_to_str(PT_PHYSICAL), buf); } if (showswap == Z_OK) { @@ -5369,16 +5841,16 @@ info_mcap(zone_dochandle_t handle, FILE *fp) int res1, res2, res3; uint64_t swap_limit; uint64_t locked_limit; - struct zone_mcaptab lookup; + uint64_t phys_limit; - bzero(&lookup, sizeof (lookup)); - res1 = zonecfg_getmcapent(handle, &lookup); + res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &phys_limit); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit); res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &locked_limit); if (res1 == Z_OK || res2 == Z_OK || res3 == Z_OK) - output_mcap(fp, &lookup, res2, swap_limit, res3, locked_limit); + output_mcap(fp, res1, phys_limit, res2, swap_limit, + res3, locked_limit); } static void @@ -5429,9 +5901,11 @@ info_func(cmd_t *cmd) FILE *fp = stdout; boolean_t need_to_close = B_FALSE; int type; - int res1, res2; + int res1, res2, res3; uint64_t swap_limit; uint64_t locked_limit; + uint64_t phys_limit; + struct stat statbuf; assert(cmd != NULL); @@ -5479,7 +5953,9 @@ info_func(cmd_t *cmd) &swap_limit); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &locked_limit); - output_mcap(fp, &in_progress_mcaptab, res1, swap_limit, + res3 = zonecfg_get_aliased_rctl(handle, + ALIAS_MAXPHYSMEM, &phys_limit); + output_mcap(fp, res3, phys_limit, res1, swap_limit, res2, locked_limit); break; case RT_ADMIN: @@ -5519,6 +5995,7 @@ info_func(cmd_t *cmd) info_iptype(handle, fp); info_hostid(handle, fp); info_fs_allowed(handle, fp); + info_uuid(fp); } info_aliased_rctl(handle, fp, ALIAS_MAXLWPS); info_aliased_rctl(handle, fp, ALIAS_MAXPROCS); @@ -5527,6 +6004,7 @@ info_func(cmd_t *cmd) info_aliased_rctl(handle, fp, ALIAS_MAXMSGIDS); info_aliased_rctl(handle, fp, ALIAS_MAXSEMIDS); info_aliased_rctl(handle, fp, ALIAS_SHARES); + info_aliased_rctl(handle, fp, ALIAS_ZFSPRI); if (!global_zone) { info_fs(handle, fp, cmd); info_net(handle, fp, cmd); @@ -5590,6 +6068,9 @@ info_func(cmd_t *cmd) case RT_SHARES: info_aliased_rctl(handle, fp, ALIAS_SHARES); break; + case RT_ZFSPRI: + info_aliased_rctl(handle, fp, ALIAS_ZFSPRI); + break; case RT_FS: info_fs(handle, fp, cmd); break; @@ -5620,6 +6101,9 @@ info_func(cmd_t *cmd) case RT_HOSTID: info_hostid(handle, fp); break; + case RT_UUID: + info_uuid(fp); + break; case RT_ADMIN: info_auth(handle, fp, cmd); break; @@ -6101,11 +6585,29 @@ verify_func(cmd_t *cmd) if (save) { if (ret_val == Z_OK) { + /* + * If the zone doesn't yet have a debug ID, set one now. + */ + if (zonecfg_get_did(handle) == -1) + zonecfg_set_did(handle); + if ((ret_val = zonecfg_save(handle)) == Z_OK) { need_to_commit = B_FALSE; (void) strlcpy(revert_zone, zone, sizeof (revert_zone)); } + + /* + * Commit a new uuid at this point since we now know the + * zone index entry will exist. + */ + if (new_uuid[0] != '\0') { + if ((err = zonecfg_set_uuid(zone, zonepath, + new_uuid)) != Z_OK) + zone_perror(zone, err, B_FALSE); + else + new_uuid[0] = '\0'; + } } else { zerr(gettext("Zone %s failed to verify"), zone); } @@ -6275,6 +6777,7 @@ end_func(cmd_t *cmd) int err, arg, res1, res2, res3; uint64_t swap_limit; uint64_t locked_limit; + uint64_t phys_limit; uint64_t proc_cap; assert(cmd != NULL); @@ -6578,8 +7081,8 @@ end_func(cmd_t *cmd) break; case RT_MCAP: /* Make sure everything was filled in. */ - res1 = strlen(in_progress_mcaptab.zone_physmem_cap) == 0 ? - Z_ERR : Z_OK; + res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, + &phys_limit); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit); res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, @@ -6595,11 +7098,6 @@ end_func(cmd_t *cmd) /* if phys & locked are both set, verify locked <= phys */ if (res1 == Z_OK && res3 == Z_OK) { - uint64_t phys_limit; - char *endp; - - phys_limit = strtoull( - in_progress_mcaptab.zone_physmem_cap, &endp, 10); if (phys_limit < locked_limit) { zerr(gettext("The %s cap must be less than or " "equal to the %s cap."), @@ -6611,23 +7109,6 @@ end_func(cmd_t *cmd) } err = Z_OK; - if (res1 == Z_OK) { - /* - * We could be ending from either an add operation - * or a select operation. Since all of the properties - * within this resource are optional, we always use - * modify on the mcap entry. zonecfg_modify_mcap() - * will handle both adding and modifying a memory cap. - */ - err = zonecfg_modify_mcap(handle, &in_progress_mcaptab); - } else if (end_op == CMD_SELECT) { - /* - * If we're ending from a select and the physical - * memory cap is empty then the user could have cleared - * the physical cap value, so try to delete the entry. - */ - (void) zonecfg_delete_mcap(handle); - } break; case RT_ADMIN: /* First make sure everything was filled in. */ @@ -7176,8 +7657,10 @@ get_execbasename(char *execfullname) int main(int argc, char *argv[]) { - int err, arg; + int err, arg, uflag = 0, zflag = 0; struct stat st; + uuid_t uuidin; + char zonename[ZONENAME_MAX + 1]; /* This must be before anything goes to stdout. */ setbuf(stdout, NULL); @@ -7204,7 +7687,7 @@ main(int argc, char *argv[]) exit(Z_OK); } - while ((arg = getopt(argc, argv, "?f:R:z:")) != EOF) { + while ((arg = getopt(argc, argv, "?f:R:z:u:")) != EOF) { switch (arg) { case '?': if (optopt == '?') @@ -7231,6 +7714,21 @@ main(int argc, char *argv[]) } zonecfg_set_root(optarg); break; + case 'u': + if (uuid_parse((char *)optarg, uuidin) == -1) + return (Z_INVALID_PROPERTY); + + if (zonecfg_get_name_by_uuid(uuidin, zonename, + ZONENAME_MAX) != Z_OK) { + zone_perror(optarg, Z_BOGUS_ZONE_NAME, B_TRUE); + usage(B_FALSE, HELP_SYNTAX); + exit(Z_USAGE); + } + + (void) strlcpy(zone, zonename, sizeof (zone)); + (void) strlcpy(revert_zone, zonename, sizeof (zone)); + uflag = 1; + break; case 'z': if (strcmp(optarg, GLOBAL_ZONENAME) == 0) { global_zone = B_TRUE; @@ -7241,6 +7739,7 @@ main(int argc, char *argv[]) } (void) strlcpy(zone, optarg, sizeof (zone)); (void) strlcpy(revert_zone, optarg, sizeof (zone)); + zflag = 1; break; default: usage(B_FALSE, HELP_USAGE); @@ -7248,7 +7747,7 @@ main(int argc, char *argv[]) } } - if (optind > argc || strcmp(zone, "") == 0) { + if (optind > argc || strcmp(zone, "") == 0 || (uflag && zflag)) { usage(B_FALSE, HELP_USAGE); exit(Z_USAGE); } diff --git a/usr/src/cmd/zonecfg/zonecfg.h b/usr/src/cmd/zonecfg/zonecfg.h index d8f8b14ce8..f8c78437ad 100644 --- a/usr/src/cmd/zonecfg/zonecfg.h +++ b/usr/src/cmd/zonecfg/zonecfg.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #ifndef _ZONECFG_H @@ -90,9 +91,11 @@ extern "C" { #define RT_ADMIN 26 #define RT_FS_ALLOWED 27 #define RT_MAXPROCS 28 /* really a rctl alias property, but for info */ +#define RT_ZFSPRI 29 /* really a rctl alias property, but for info */ +#define RT_UUID 30 /* really a property, but for info */ #define RT_MIN RT_UNKNOWN -#define RT_MAX RT_MAXPROCS +#define RT_MAX RT_UUID /* property types: increment PT_MAX when expanding this list */ #define PT_UNKNOWN 0 @@ -137,9 +140,15 @@ extern "C" { #define PT_FS_ALLOWED 39 #define PT_MAXPROCS 40 #define PT_ALLOWED_ADDRESS 41 +#define PT_ZFSPRI 42 +#define PT_MAC 43 +#define PT_VLANID 44 +#define PT_GNIC 45 +#define PT_NPROP 46 +#define PT_UUID 47 #define PT_MIN PT_UNKNOWN -#define PT_MAX PT_ALLOWED_ADDRESS +#define PT_MAX PT_UUID #define MAX_EQ_PROP_PAIRS 3 diff --git a/usr/src/cmd/zonecfg/zonecfg_grammar.y b/usr/src/cmd/zonecfg/zonecfg_grammar.y index d7f11b6a46..13a17876b7 100644 --- a/usr/src/cmd/zonecfg/zonecfg_grammar.y +++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, Joyent Inc. All rights reserved. */ /* @@ -136,6 +137,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS %token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN USER AUTHS MAXPROCS +%token ZFSPRI MAC VLANID GNIC NPROP UUID %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET property_value OPEN_PAREN CLOSE_PAREN COMMA simple_prop_val @@ -145,7 +147,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV BOOTARGS VALUE PRIV LIMIT ACTION BRAND SCHED IPTYPE DEFROUTER HOSTID USER AUTHS FS_ALLOWED - ALLOWED_ADDRESS + ALLOWED_ADDRESS MAC VLANID GNIC NPROP UUID %type <cmd> command %type <cmd> add_command ADD %type <cmd> cancel_command CANCEL @@ -650,6 +652,24 @@ info_command: INFO $$->cmd_res_type = RT_FS_ALLOWED; $$->cmd_prop_nv_pairs = 0; } + | INFO UUID + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &info_func; + $$->cmd_res_type = RT_UUID; + $$->cmd_prop_nv_pairs = 0; + } + | INFO ZFSPRI + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &info_func; + $$->cmd_res_type = RT_ZFSPRI; + $$->cmd_prop_nv_pairs = 0; + } | INFO resource_type property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -734,6 +754,19 @@ remove_command: REMOVE $$->cmd_prop_name[0] = $2; $$->cmd_property_ptr[0] = &property[0]; } + | REMOVE TOKEN property_name property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 1; + $$->cmd_prop_name[0] = $3; + $$->cmd_property_ptr[0] = &property[0]; + } | REMOVE resource_type property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -745,6 +778,20 @@ remove_command: REMOVE $$->cmd_prop_name[0] = $3; $$->cmd_property_ptr[0] = &property[0]; } + | REMOVE TOKEN resource_type property_name EQUAL property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_res_type = $3; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 1; + $$->cmd_prop_name[0] = $4; + $$->cmd_property_ptr[0] = &property[0]; + } | REMOVE resource_type property_name EQUAL property_value property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -758,6 +805,22 @@ remove_command: REMOVE $$->cmd_prop_name[1] = $6; $$->cmd_property_ptr[1] = &property[1]; } + | REMOVE TOKEN resource_type property_name EQUAL property_value property_name EQUAL property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_res_type = $3; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 2; + $$->cmd_prop_name[0] = $4; + $$->cmd_property_ptr[0] = &property[0]; + $$->cmd_prop_name[1] = $7; + $$->cmd_property_ptr[1] = &property[1]; + } | REMOVE resource_type property_name EQUAL property_value property_name EQUAL property_value property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -773,6 +836,24 @@ remove_command: REMOVE $$->cmd_prop_name[2] = $9; $$->cmd_property_ptr[2] = &property[2]; } + | REMOVE TOKEN resource_type property_name EQUAL property_value property_name EQUAL property_value property_name EQUAL property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_res_type = $3; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 3; + $$->cmd_prop_name[0] = $4; + $$->cmd_property_ptr[0] = &property[0]; + $$->cmd_prop_name[1] = $7; + $$->cmd_property_ptr[1] = &property[1]; + $$->cmd_prop_name[2] = $10; + $$->cmd_property_ptr[2] = &property[2]; + } revert_command: REVERT { @@ -976,6 +1057,10 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | ALLOWED_ADDRESS { $$ = PT_ALLOWED_ADDRESS; } | PHYSICAL { $$ = PT_PHYSICAL; } | DEFROUTER { $$ = PT_DEFROUTER; } + | MAC { $$ = PT_MAC; } + | VLANID { $$ = PT_VLANID; } + | GNIC { $$ = PT_GNIC; } + | NPROP { $$ = PT_NPROP; } | NAME { $$ = PT_NAME; } | VALUE { $$ = PT_VALUE; } | MATCH { $$ = PT_MATCH; } @@ -999,6 +1084,8 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | USER { $$ = PT_USER; } | AUTHS { $$ = PT_AUTHS; } | FS_ALLOWED { $$ = PT_FS_ALLOWED; } + | UUID { $$ = PT_UUID; } + | ZFSPRI { $$ = PT_ZFSPRI; } /* * The grammar builds data structures from the bottom up. Thus various diff --git a/usr/src/cmd/zonecfg/zonecfg_lex.l b/usr/src/cmd/zonecfg/zonecfg_lex.l index 6a0b577b75..328a75c922 100644 --- a/usr/src/cmd/zonecfg/zonecfg_lex.l +++ b/usr/src/cmd/zonecfg/zonecfg_lex.l @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #include <assert.h> @@ -57,10 +58,11 @@ extern void yyerror(char *s); static char *create_token(char *s); %} -%a 7000 +%a 8000 %p 5000 %e 2000 %n 1000 +%o 13000 %{ /* @@ -236,6 +238,18 @@ static char *create_token(char *s); <TSTATE>defrouter { return DEFROUTER; } <CSTATE>defrouter { return DEFROUTER; } +<TSTATE>mac-addr { return MAC; } +<CSTATE>mac-addr { return MAC; } + +<TSTATE>vlan-id { return VLANID; } +<CSTATE>vlan-id { return VLANID; } + +<TSTATE>global-nic { return GNIC; } +<CSTATE>global-nic { return GNIC; } + +<TSTATE>property { return NPROP; } +<CSTATE>property { return NPROP; } + <TSTATE>dir { return DIR; } <CSTATE>dir { return DIR; } @@ -308,6 +322,12 @@ static char *create_token(char *s); <TSTATE>fs-allowed { return FS_ALLOWED; } <CSTATE>fs-allowed { return FS_ALLOWED; } +<TSTATE>uuid { return UUID; } +<CSTATE>uuid { return UUID; } + +<TSTATE>zfs-io-priority { return ZFSPRI; } +<CSTATE>zfs-io-priority { return ZFSPRI; } + <TSTATE>= { return EQUAL; } <LSTATE>= { return EQUAL; } <CSTATE>= { return EQUAL; } @@ -357,6 +377,13 @@ static char *create_token(char *s); return TOKEN; } +<CSTATE>\"[^\"\n]*[\"\n] { + yylval.strval = create_token(yytext + 1); + if (yylval.strval[yyleng - 2] == '"') + yylval.strval[yyleng - 2] = 0; + return TOKEN; + } + <TSTATE>\"[^\"\n]*[\"\n] { yylval.strval = create_token(yytext + 1); if (yylval.strval[yyleng - 2] == '"') diff --git a/usr/src/cmd/zonename/Makefile b/usr/src/cmd/zonename/Makefile index 566e893a67..3a51952455 100644 --- a/usr/src/cmd/zonename/Makefile +++ b/usr/src/cmd/zonename/Makefile @@ -28,8 +28,10 @@ # PROG= zonename +OBJS= zonename.o include ../Makefile.cmd +include ../Makefile.ctf LDLIBS += -lzonecfg @@ -37,6 +39,10 @@ LDLIBS += -lzonecfg all: $(PROG) +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + install: all $(ROOTSBINPROG) $(RM) $(ROOTPROG) $(SYMLINK) ../../sbin/$(PROG) $(ROOTPROG) @@ -44,6 +50,10 @@ install: all $(ROOTSBINPROG) check: $(PROG).c $(CSTYLE) -pP $(PROG).c +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + clean: lint: lint_PROG diff --git a/usr/src/cmd/zonestat/zonestatd/zonestatd.c b/usr/src/cmd/zonestat/zonestatd/zonestatd.c index b764551131..6c293bcc0e 100644 --- a/usr/src/cmd/zonestat/zonestatd/zonestatd.c +++ b/usr/src/cmd/zonestat/zonestatd/zonestatd.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <alloca.h> #include <assert.h> @@ -2190,7 +2191,7 @@ zsd_get_zone_rctl_usage(char *name) return (rctlblk_get_value(rblk)); } -#define ZSD_NUM_RCTL_VALS 19 +#define ZSD_NUM_RCTL_VALS 20 /* * Fetch the limit information for a zone. This uses zone_enter() as the @@ -2237,12 +2238,6 @@ zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares, *msgids = 0; *lofi = 0; - /* Get the ram cap first since it is a zone attr */ - ret = zone_getattr(zone->zsz_id, ZONE_ATTR_PHYS_MCAP, - ram_cap, sizeof (*ram_cap)); - if (ret < 0 || *ram_cap == 0) - *ram_cap = ZS_LIMIT_NONE; - /* Get the zone's default scheduling class */ ret = zone_getattr(zone->zsz_id, ZONE_ATTR_SCHED_CLASS, class, sizeof (class)); @@ -2298,6 +2293,7 @@ zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares, vals[i++] = zsd_get_zone_rctl_usage("zone.max-msg-ids"); vals[i++] = zsd_get_zone_rctl_limit("zone.max-lofi"); vals[i++] = zsd_get_zone_rctl_usage("zone.max-lofi"); + vals[i++] = zsd_get_zone_rctl_usage("zone.max-physical-memory"); if (write(p[1], vals, ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) != ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) { @@ -2342,6 +2338,7 @@ zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares, *msgids = vals[i++]; *lofi_cap = vals[i++]; *lofi = vals[i++]; + *ram_cap = vals[i++]; /* Interpret maximum values as no cap */ if (*cpu_cap == UINT32_MAX || *cpu_cap == 0) diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 143ee2155d..855ec12c17 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -25,6 +25,7 @@ * Copyright (c) 2011, 2014 by Delphix. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. */ #include <assert.h> |