summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/Makefile25
-rw-r--r--usr/src/cmd/Makefile.check3
-rw-r--r--usr/src/cmd/cmd-inet/etc/services3
-rw-r--r--usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel4
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c37
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt7
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c22
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c2
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/Makefile2
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/Makefile2
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile2
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c32
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile5
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c10
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h4
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c13
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c4
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c5
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c68
-rw-r--r--usr/src/cmd/column/Makefile43
-rw-r--r--usr/src/cmd/column/THIRDPARTYLICENSE26
-rw-r--r--usr/src/cmd/column/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/cmd/column/column.c335
-rw-r--r--usr/src/cmd/coreadm/coreadm.xml8
-rw-r--r--usr/src/cmd/cron/Makefile36
-rw-r--r--usr/src/cmd/cron/cron.c154
-rw-r--r--usr/src/cmd/cron/cron.h3
-rw-r--r--usr/src/cmd/cron/crontab.c38
-rw-r--r--usr/src/cmd/ctfdiff/Makefile33
-rw-r--r--usr/src/cmd/ctfdiff/ctfdiff.c508
-rw-r--r--usr/src/cmd/ctfdump/Makefile33
-rw-r--r--usr/src/cmd/ctfdump/ctfdump.c803
-rw-r--r--usr/src/cmd/ctfmerge/Makefile33
-rw-r--r--usr/src/cmd/ctfmerge/ctfmerge.c487
-rw-r--r--usr/src/cmd/devfsadm/Makefile.com1
-rw-r--r--usr/src/cmd/devfsadm/devlink.tab.sh5
-rw-r--r--usr/src/cmd/devfsadm/i386/Makefile3
-rw-r--r--usr/src/cmd/devfsadm/i386/lx_link_i386.c86
-rw-r--r--usr/src/cmd/devfsadm/misc_link.c51
-rw-r--r--usr/src/cmd/dladm/Makefile3
-rw-r--r--usr/src/cmd/dladm/dladm.c957
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_db.c189
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_door.c66
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_impl.h6
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_main.c66
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_util.c17
-rw-r--r--usr/src/cmd/dlmgmtd/svc-dlmgmtd9
-rw-r--r--usr/src/cmd/dlstat/dlstat.c70
-rw-r--r--usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile3
-rw-r--r--usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl2
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/Makefile8
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/java_api/Makefile3
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d4
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh69
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c7
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh68
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d4
-rw-r--r--usr/src/cmd/flowadm/flowadm.c51
-rw-r--r--usr/src/cmd/flowstat/flowstat.c21
-rw-r--r--usr/src/cmd/fs.d/Makefile5
-rw-r--r--usr/src/cmd/fs.d/bootfs/Makefile21
-rw-r--r--usr/src/cmd/fs.d/bootfs/mount.c139
-rw-r--r--usr/src/cmd/fs.d/hyprlofs/Makefile42
-rw-r--r--usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile30
-rw-r--r--usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c244
-rw-r--r--usr/src/cmd/fs.d/hyprlofs/mount/Makefile31
-rw-r--r--usr/src/cmd/fs.d/hyprlofs/mount/mount.c148
-rw-r--r--usr/src/cmd/fs.d/lxproc/Makefile32
-rw-r--r--usr/src/cmd/fs.d/lxproc/mount.c140
-rw-r--r--usr/src/cmd/fs.d/nfs/lib/smfcfg.c20
-rw-r--r--usr/src/cmd/fs.d/nfs/lib/smfcfg.h2
-rw-r--r--usr/src/cmd/fs.d/nfs/mount/Makefile14
-rw-r--r--usr/src/cmd/fs.d/nfs/mount/mount.c2
-rw-r--r--usr/src/cmd/fs.d/nfs/mountd/mountd.c8
-rw-r--r--usr/src/cmd/fs.d/nfs/nfsd/nfsd.c8
-rw-r--r--usr/src/cmd/fs.d/nfs/svc/nfs-server4
-rw-r--r--usr/src/cmd/fs.d/nfs/umount/umount.c2
-rw-r--r--usr/src/cmd/halt/halt.c19
-rw-r--r--usr/src/cmd/ibd_upgrade/ibd_delete_link.c2
-rw-r--r--usr/src/cmd/init/Makefile10
-rw-r--r--usr/src/cmd/init/init.c40
-rw-r--r--usr/src/cmd/initpkg/mountall.sh5
-rw-r--r--usr/src/cmd/initpkg/shutdown.sh2
-rw-r--r--usr/src/cmd/initpkg/umountall.sh4
-rw-r--r--usr/src/cmd/ipf/tools/Makefile.tools1
-rw-r--r--usr/src/cmd/ipf/tools/ipfstat.c4
-rw-r--r--usr/src/cmd/krb5/kadmin/Makefile2
-rw-r--r--usr/src/cmd/ksh/Makefile.com2
-rw-r--r--usr/src/cmd/localedef/Makefile14
-rw-r--r--usr/src/cmd/lofiadm/main.c28
-rw-r--r--usr/src/cmd/lp/terminfo/40.ti68
-rw-r--r--usr/src/cmd/lp/terminfo/44x.ti57
-rw-r--r--usr/src/cmd/lp/terminfo/45x.ti42
-rw-r--r--usr/src/cmd/lp/terminfo/477.ti181
-rw-r--r--usr/src/cmd/lp/terminfo/47x.ti134
-rw-r--r--usr/src/cmd/lp/terminfo/495.ti78
-rw-r--r--usr/src/cmd/lp/terminfo/53x0.ti81
-rw-r--r--usr/src/cmd/lp/terminfo/57x.ti105
-rw-r--r--usr/src/cmd/lp/terminfo/58x.ti103
-rw-r--r--usr/src/cmd/lp/terminfo/593.ti73
-rw-r--r--usr/src/cmd/lp/terminfo/Makefile77
-rw-r--r--usr/src/cmd/lp/terminfo/PS.ti36
-rw-r--r--usr/src/cmd/lp/terminfo/citoh.ti127
-rw-r--r--usr/src/cmd/lp/terminfo/daisy.ti126
-rw-r--r--usr/src/cmd/lp/terminfo/dec.ti130
-rw-r--r--usr/src/cmd/lp/terminfo/epson.ti301
-rw-r--r--usr/src/cmd/lp/terminfo/hplaser.ti122
-rw-r--r--usr/src/cmd/lp/terminfo/ibm.ti353
-rw-r--r--usr/src/cmd/lp/terminfo/unknown.ti30
-rw-r--r--usr/src/cmd/machid/Makefile80
-rw-r--r--usr/src/cmd/machid/machid.c137
-rw-r--r--usr/src/cmd/man/Makefile7
-rw-r--r--usr/src/cmd/man/Makefile.com23
-rw-r--r--usr/src/cmd/mdb/Makefile.common3
-rw-r--r--usr/src/cmd/mdb/Makefile.module24
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c10
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_promif.c7
-rw-r--r--usr/src/cmd/mdb/common/libstandctf/ctf_subr.c9
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_cmds.c4
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.c43
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.h4
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf_open.c27
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_debug.c4
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_debug.h4
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_demangle.c11
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_main.c13
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.h27
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_print.c45
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_tab.c24
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_termio.c79
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_typedef.c38
-rw-r--r--usr/src/cmd/mdb/common/modules/dtrace/dtrace.c3
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c193
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/netstack.c30
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/netstack.h3
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.c67
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.h4
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/libc.c28
-rw-r--r--usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c4
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/mdb_v8.c5709
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/mdb_v8_cfg.c728
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/v8cfg.h55
-rw-r--r--usr/src/cmd/mdb/common/modules/v8/v8dbg.h91
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/unix/amd64/Makefile1
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/unix/ia32/Makefile1
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/unix/unix.c190
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.h36
-rw-r--r--usr/src/cmd/mdb/i86pc/modules/unix/unix_sup.s62
-rw-r--r--usr/src/cmd/mdb/i86xpv/modules/unix/amd64/Makefile1
-rw-r--r--usr/src/cmd/mdb/i86xpv/modules/unix/ia32/Makefile1
-rw-r--r--usr/src/cmd/mdb/intel/amd64/libumem/Makefile1
-rw-r--r--usr/src/cmd/mdb/intel/amd64/v8/Makefile45
-rw-r--r--usr/src/cmd/mdb/intel/ia32/libumem/Makefile1
-rw-r--r--usr/src/cmd/mdb/intel/ia32/v8/Makefile44
-rw-r--r--usr/src/cmd/mdb/sparc/v7/libumem/Makefile1
-rw-r--r--usr/src/cmd/mdb/sparc/v9/libumem/Makefile1
-rwxr-xr-x[-rw-r--r--]usr/src/cmd/mdb/test/mtest.sh0
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.dellist.mdb.out0
-rw-r--r--usr/src/cmd/mdb/test/typedef/tst.emptylist.mdb.out0
-rw-r--r--usr/src/cmd/nicstat/Makefile31
-rw-r--r--usr/src/cmd/nicstat/nicstat.pl424
-rw-r--r--usr/src/cmd/nscd/Makefile1
-rw-r--r--usr/src/cmd/nscd/svc-nscd4
-rw-r--r--usr/src/cmd/passwd/Makefile2
-rw-r--r--usr/src/cmd/pgrep/pgrep.c8
-rw-r--r--usr/src/cmd/prstat/prstat.c48
-rw-r--r--usr/src/cmd/prstat/prstat.h2
-rw-r--r--usr/src/cmd/prstat/prutil.c3
-rw-r--r--usr/src/cmd/prtconf/prtconf.c52
-rw-r--r--usr/src/cmd/prtconf/prtconf.h1
-rw-r--r--usr/src/cmd/ptools/Makefile.bld41
-rw-r--r--usr/src/cmd/ptools/common/ptools_common.c50
-rw-r--r--usr/src/cmd/ptools/common/ptools_common.h36
-rw-r--r--usr/src/cmd/ptools/pargs/pargs.c7
-rw-r--r--usr/src/cmd/ptools/pfiles/pfiles.c33
-rw-r--r--usr/src/cmd/ptools/pflags/pflags.c5
-rw-r--r--usr/src/cmd/ptools/pmadvise/pmadvise.c14
-rw-r--r--usr/src/cmd/ptools/pmap/pmap.c10
-rw-r--r--usr/src/cmd/ptools/pmap/pmap_common.c5
-rw-r--r--usr/src/cmd/ptools/preap/preap.c8
-rw-r--r--usr/src/cmd/ptools/psig/psig.c33
-rw-r--r--usr/src/cmd/ptools/ptime/ptime.c118
-rw-r--r--usr/src/cmd/ptools/ptree/ptree.c17
-rw-r--r--usr/src/cmd/ptools/pwait/pwait.c29
-rw-r--r--usr/src/cmd/ptools/pwdx/pwdx.c7
-rw-r--r--usr/src/cmd/rcap/common/utils.c75
-rw-r--r--usr/src/cmd/rcap/common/utils.h2
-rw-r--r--usr/src/cmd/rcap/rcapadm/rcapadm.c16
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c141
-rw-r--r--usr/src/cmd/rcap/rcapd/rcapd_scanner.c3
-rw-r--r--usr/src/cmd/rcap/rcapstat/rcapstat.c7
-rw-r--r--usr/src/cmd/rcm_daemon/Makefile.com4
-rw-r--r--usr/src/cmd/savecore/savecore.c46
-rw-r--r--usr/src/cmd/sed/main.c27
-rw-r--r--usr/src/cmd/sendmail/src/Makefile2
-rw-r--r--usr/src/cmd/sgs/elfdump/Makefile.targ2
-rw-r--r--usr/src/cmd/sgs/elfdump/amd64/Makefile2
-rw-r--r--usr/src/cmd/sgs/elfdump/i386/Makefile2
-rw-r--r--usr/src/cmd/sgs/lex/Makefile.targ2
-rw-r--r--usr/src/cmd/sgs/lex/common/main.c45
-rw-r--r--usr/src/cmd/sgs/lex/common/once.h12
-rw-r--r--usr/src/cmd/sgs/libconv/common/corenote.c24
-rw-r--r--usr/src/cmd/sgs/libconv/common/corenote.msg6
-rw-r--r--usr/src/cmd/sgs/librtld_db/common/librtld_db.msg6
-rw-r--r--usr/src/cmd/sgs/librtld_db/common/rd_elf.c28
-rw-r--r--usr/src/cmd/sgs/rtld/common/_rtld.h4
-rw-r--r--usr/src/cmd/sgs/rtld/common/analyze.c50
-rw-r--r--usr/src/cmd/sgs/rtld/common/globals.c3
-rw-r--r--usr/src/cmd/sgs/rtld/common/rtld.msg7
-rw-r--r--usr/src/cmd/sgs/rtld/common/setup.c10
-rw-r--r--usr/src/cmd/sgs/rtld/common/util.c16
-rw-r--r--usr/src/cmd/sgs/yacc/Makefile.targ2
-rw-r--r--usr/src/cmd/sgs/yacc/common/dextern.h19
-rw-r--r--usr/src/cmd/sgs/yacc/common/y1.c36
-rw-r--r--usr/src/cmd/sgs/yacc/common/y2.c7
-rw-r--r--usr/src/cmd/ssh/Makefile2
-rw-r--r--usr/src/cmd/ssh/include/key.h15
-rw-r--r--usr/src/cmd/ssh/include/servconf.h1
-rw-r--r--usr/src/cmd/ssh/libssh/common/canohost.c35
-rw-r--r--usr/src/cmd/ssh/libssh/common/key.c17
-rw-r--r--usr/src/cmd/ssh/sftp-server/Makefile2
-rw-r--r--usr/src/cmd/ssh/sftp/Makefile2
-rw-r--r--usr/src/cmd/ssh/ssh-add/Makefile2
-rw-r--r--usr/src/cmd/ssh/ssh-agent/Makefile2
-rw-r--r--usr/src/cmd/ssh/ssh-keygen/Makefile2
-rw-r--r--usr/src/cmd/ssh/ssh-keygen/ssh-keygen.c277
-rw-r--r--usr/src/cmd/ssh/ssh-keyscan/Makefile2
-rw-r--r--usr/src/cmd/ssh/ssh-keysign/Makefile2
-rw-r--r--usr/src/cmd/ssh/ssh/Makefile2
-rw-r--r--usr/src/cmd/ssh/sshd/Makefile2
-rw-r--r--usr/src/cmd/ssh/sshd/auth2-pubkey.c109
-rw-r--r--usr/src/cmd/ssh/sshd/servconf.c9
-rw-r--r--usr/src/cmd/ssh/sshd/sshlogin.c3
-rw-r--r--usr/src/cmd/stat/Makefile11
-rw-r--r--usr/src/cmd/stat/arcstat/Makefile1
-rw-r--r--[-rwxr-xr-x]usr/src/cmd/stat/arcstat/arcstat.pl0
-rw-r--r--usr/src/cmd/stat/vfsstat/Makefile41
-rw-r--r--usr/src/cmd/stat/vfsstat/vfsstat.pl227
-rw-r--r--usr/src/cmd/stat/ziostat/Makefile41
-rwxr-xr-xusr/src/cmd/stat/ziostat/ziostat.pl204
-rw-r--r--usr/src/cmd/svc/configd/rc_node.c3
-rw-r--r--usr/src/cmd/svc/milestone/net-routing-setup23
-rw-r--r--usr/src/cmd/svc/milestone/network-location.xml2
-rw-r--r--usr/src/cmd/svc/milestone/network-routing-setup.xml14
-rw-r--r--usr/src/cmd/svc/milestone/network.xml8
-rw-r--r--usr/src/cmd/svc/milestone/single-user.xml2
-rw-r--r--usr/src/cmd/svc/shell/smf_include.sh6
-rw-r--r--usr/src/cmd/svc/startd/graph.c26
-rw-r--r--usr/src/cmd/svc/startd/method.c96
-rw-r--r--usr/src/cmd/svc/startd/startd.c130
-rw-r--r--usr/src/cmd/svc/startd/startd.h4
-rw-r--r--usr/src/cmd/svc/svcadm/Makefile7
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_libscf.c240
-rw-r--r--usr/src/cmd/svc/svcs/Makefile2
-rw-r--r--usr/src/cmd/svc/svcs/explain.c4
-rw-r--r--usr/src/cmd/svc/svcs/svcs.c94
-rw-r--r--usr/src/cmd/svr4pkg/pkgadd/Makefile2
-rw-r--r--usr/src/cmd/svr4pkg/pkgadm/Makefile2
-rw-r--r--usr/src/cmd/tail/Makefile5
-rw-r--r--usr/src/cmd/tar/Makefile2
-rw-r--r--usr/src/cmd/tar/tar.c38
-rw-r--r--usr/src/cmd/truss/codes.c16
-rw-r--r--usr/src/cmd/truss/expound.c16
-rw-r--r--usr/src/cmd/truss/print.c14
-rw-r--r--usr/src/cmd/truss/systable.c8
-rw-r--r--usr/src/cmd/varpd/Makefile74
-rw-r--r--usr/src/cmd/varpd/svc-varpd35
-rw-r--r--usr/src/cmd/varpd/varpd.c481
-rw-r--r--usr/src/cmd/varpd/varpd.xml52
-rw-r--r--usr/src/cmd/vi/port/Makefile3
-rw-r--r--usr/src/cmd/vi/port/ex_cmdsub.c2
-rw-r--r--usr/src/cmd/vndadm/Makefile65
-rw-r--r--usr/src/cmd/vndadm/test/Makefile19
-rw-r--r--usr/src/cmd/vndadm/test/Makefile.com43
-rw-r--r--usr/src/cmd/vndadm/test/Makefile.subdirs29
-rw-r--r--usr/src/cmd/vndadm/test/Makefile.targ59
-rw-r--r--usr/src/cmd/vndadm/test/scripts/Makefile28
-rwxr-xr-xusr/src/cmd/vndadm/test/scripts/vndtest.ksh300
-rw-r--r--usr/src/cmd/vndadm/test/tst/Makefile18
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/Makefile34
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh33
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh30
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out2
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh25
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh34
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh25
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh24
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh24
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh24
-rw-r--r--usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh24
-rw-r--r--usr/src/cmd/vndadm/test/tst/dld/Makefile27
-rw-r--r--usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh31
-rw-r--r--usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh29
-rw-r--r--usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh27
-rw-r--r--usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh27
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/Makefile49
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c63
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c67
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c119
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c82
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c69
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.link.c76
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c90
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c96
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c69
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c69
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c70
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c75
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c77
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c77
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c83
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c69
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c63
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c79
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c76
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c78
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c100
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh54
-rw-r--r--usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c88
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/Makefile44
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.badlink.c39
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c76
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c63
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.badzone.c43
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.basic.c49
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.enomem.c91
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c80
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.open.c56
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.propiter.c79
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c63
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/err.badclose.c33
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c49
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c30
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out37
-rw-r--r--usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c50
-rw-r--r--usr/src/cmd/vndadm/vndadm.c872
-rw-r--r--usr/src/cmd/vndstat/Makefile33
-rw-r--r--usr/src/cmd/vndstat/vndstat.c542
-rw-r--r--usr/src/cmd/zfs/zfs_main.c142
-rw-r--r--usr/src/cmd/zlogin/zlogin.c445
-rw-r--r--usr/src/cmd/zoneadm/Makefile11
-rw-r--r--usr/src/cmd/zoneadm/svc-zones2
-rw-r--r--usr/src/cmd/zoneadm/zfs.c16
-rw-r--r--usr/src/cmd/zoneadm/zoneadm.c210
-rw-r--r--usr/src/cmd/zoneadm/zones.xml10
-rw-r--r--usr/src/cmd/zoneadmd/Makefile49
-rw-r--r--usr/src/cmd/zoneadmd/Makefile.com70
-rw-r--r--usr/src/cmd/zoneadmd/amd64/Makefile31
-rw-r--r--usr/src/cmd/zoneadmd/i386/Makefile30
-rw-r--r--usr/src/cmd/zoneadmd/mcap.c1182
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c519
-rw-r--r--usr/src/cmd/zoneadmd/zcons.c144
-rw-r--r--usr/src/cmd/zoneadmd/zfd.c1344
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.c636
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.h27
-rw-r--r--usr/src/cmd/zonecfg/Makefile3
-rw-r--r--usr/src/cmd/zonecfg/zonecfg.c889
-rw-r--r--usr/src/cmd/zonecfg/zonecfg.h13
-rw-r--r--usr/src/cmd/zonecfg/zonecfg_grammar.y89
-rw-r--r--usr/src/cmd/zonecfg/zonecfg_lex.l29
-rw-r--r--usr/src/cmd/zonename/Makefile10
-rw-r--r--usr/src/cmd/zonestat/zonestatd/zonestatd.c11
-rw-r--r--usr/src/cmd/zpool/zpool_main.c1
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", &regfp) != 0 ||
+ mdb_getareg(1, "rip", &regip) != 0) {
+#else
+#ifdef __i386
+ if (mdb_getareg(1, "ebp", &regfp) != 0 ||
+ mdb_getareg(1, "eip", &regip) != 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>